PageRenderTime 68ms CodeModel.GetById 47ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/base/power/trace.c

https://bitbucket.org/cresqo/cm7-p500-kernel
C | 231 lines | 131 code | 25 blank | 75 comment | 10 complexity | 99b0b6efbadefde8941ab307777840db MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
  1. /*
  2. * drivers/base/power/trace.c
  3. *
  4. * Copyright (C) 2006 Linus Torvalds
  5. *
  6. * Trace facility for suspend/resume problems, when none of the
  7. * devices may be working.
  8. */
  9. #include <linux/resume-trace.h>
  10. #include <linux/rtc.h>
  11. #include <asm/rtc.h>
  12. #include "power.h"
  13. /*
  14. * Horrid, horrid, horrid.
  15. *
  16. * It turns out that the _only_ piece of hardware that actually
  17. * keeps its value across a hard boot (and, more importantly, the
  18. * POST init sequence) is literally the realtime clock.
  19. *
  20. * Never mind that an RTC chip has 114 bytes (and often a whole
  21. * other bank of an additional 128 bytes) of nice SRAM that is
  22. * _designed_ to keep data - the POST will clear it. So we literally
  23. * can just use the few bytes of actual time data, which means that
  24. * we're really limited.
  25. *
  26. * It means, for example, that we can't use the seconds at all
  27. * (since the time between the hang and the boot might be more
  28. * than a minute), and we'd better not depend on the low bits of
  29. * the minutes either.
  30. *
  31. * There are the wday fields etc, but I wouldn't guarantee those
  32. * are dependable either. And if the date isn't valid, either the
  33. * hw or POST will do strange things.
  34. *
  35. * So we're left with:
  36. * - year: 0-99
  37. * - month: 0-11
  38. * - day-of-month: 1-28
  39. * - hour: 0-23
  40. * - min: (0-30)*2
  41. *
  42. * Giving us a total range of 0-16128000 (0xf61800), ie less
  43. * than 24 bits of actual data we can save across reboots.
  44. *
  45. * And if your box can't boot in less than three minutes,
  46. * you're screwed.
  47. *
  48. * Now, almost 24 bits of data is pitifully small, so we need
  49. * to be pretty dense if we want to use it for anything nice.
  50. * What we do is that instead of saving off nice readable info,
  51. * we save off _hashes_ of information that we can hopefully
  52. * regenerate after the reboot.
  53. *
  54. * In particular, this means that we might be unlucky, and hit
  55. * a case where we have a hash collision, and we end up not
  56. * being able to tell for certain exactly which case happened.
  57. * But that's hopefully unlikely.
  58. *
  59. * What we do is to take the bits we can fit, and split them
  60. * into three parts (16*997*1009 = 16095568), and use the values
  61. * for:
  62. * - 0-15: user-settable
  63. * - 0-996: file + line number
  64. * - 0-1008: device
  65. */
  66. #define USERHASH (16)
  67. #define FILEHASH (997)
  68. #define DEVHASH (1009)
  69. #define DEVSEED (7919)
  70. static unsigned int dev_hash_value;
  71. static int set_magic_time(unsigned int user, unsigned int file, unsigned int device)
  72. {
  73. unsigned int n = user + USERHASH*(file + FILEHASH*device);
  74. // June 7th, 2006
  75. static struct rtc_time time = {
  76. .tm_sec = 0,
  77. .tm_min = 0,
  78. .tm_hour = 0,
  79. .tm_mday = 7,
  80. .tm_mon = 5, // June - counting from zero
  81. .tm_year = 106,
  82. .tm_wday = 3,
  83. .tm_yday = 160,
  84. .tm_isdst = 1
  85. };
  86. time.tm_year = (n % 100);
  87. n /= 100;
  88. time.tm_mon = (n % 12);
  89. n /= 12;
  90. time.tm_mday = (n % 28) + 1;
  91. n /= 28;
  92. time.tm_hour = (n % 24);
  93. n /= 24;
  94. time.tm_min = (n % 20) * 3;
  95. n /= 20;
  96. set_rtc_time(&time);
  97. return n ? -1 : 0;
  98. }
  99. static unsigned int read_magic_time(void)
  100. {
  101. struct rtc_time time;
  102. unsigned int val;
  103. get_rtc_time(&time);
  104. printk("Time: %2d:%02d:%02d Date: %02d/%02d/%02d\n",
  105. time.tm_hour, time.tm_min, time.tm_sec,
  106. time.tm_mon + 1, time.tm_mday, time.tm_year % 100);
  107. val = time.tm_year; /* 100 years */
  108. if (val > 100)
  109. val -= 100;
  110. val += time.tm_mon * 100; /* 12 months */
  111. val += (time.tm_mday-1) * 100 * 12; /* 28 month-days */
  112. val += time.tm_hour * 100 * 12 * 28; /* 24 hours */
  113. val += (time.tm_min / 3) * 100 * 12 * 28 * 24; /* 20 3-minute intervals */
  114. return val;
  115. }
  116. /*
  117. * This is just the sdbm hash function with a user-supplied
  118. * seed and final size parameter.
  119. */
  120. static unsigned int hash_string(unsigned int seed, const char *data, unsigned int mod)
  121. {
  122. unsigned char c;
  123. while ((c = *data++) != 0) {
  124. seed = (seed << 16) + (seed << 6) - seed + c;
  125. }
  126. return seed % mod;
  127. }
  128. void set_trace_device(struct device *dev)
  129. {
  130. dev_hash_value = hash_string(DEVSEED, dev_name(dev), DEVHASH);
  131. }
  132. EXPORT_SYMBOL(set_trace_device);
  133. /*
  134. * We could just take the "tracedata" index into the .tracedata
  135. * section instead. Generating a hash of the data gives us a
  136. * chance to work across kernel versions, and perhaps more
  137. * importantly it also gives us valid/invalid check (ie we will
  138. * likely not give totally bogus reports - if the hash matches,
  139. * it's not any guarantee, but it's a high _likelihood_ that
  140. * the match is valid).
  141. */
  142. void generate_resume_trace(const void *tracedata, unsigned int user)
  143. {
  144. unsigned short lineno = *(unsigned short *)tracedata;
  145. const char *file = *(const char **)(tracedata + 2);
  146. unsigned int user_hash_value, file_hash_value;
  147. user_hash_value = user % USERHASH;
  148. file_hash_value = hash_string(lineno, file, FILEHASH);
  149. set_magic_time(user_hash_value, file_hash_value, dev_hash_value);
  150. }
  151. EXPORT_SYMBOL(generate_resume_trace);
  152. extern char __tracedata_start, __tracedata_end;
  153. static int show_file_hash(unsigned int value)
  154. {
  155. int match;
  156. char *tracedata;
  157. match = 0;
  158. for (tracedata = &__tracedata_start ; tracedata < &__tracedata_end ;
  159. tracedata += 2 + sizeof(unsigned long)) {
  160. unsigned short lineno = *(unsigned short *)tracedata;
  161. const char *file = *(const char **)(tracedata + 2);
  162. unsigned int hash = hash_string(lineno, file, FILEHASH);
  163. if (hash != value)
  164. continue;
  165. printk(" hash matches %s:%u\n", file, lineno);
  166. match++;
  167. }
  168. return match;
  169. }
  170. static int show_dev_hash(unsigned int value)
  171. {
  172. int match = 0;
  173. struct list_head *entry = dpm_list.prev;
  174. while (entry != &dpm_list) {
  175. struct device * dev = to_device(entry);
  176. unsigned int hash = hash_string(DEVSEED, dev_name(dev), DEVHASH);
  177. if (hash == value) {
  178. dev_info(dev, "hash matches\n");
  179. match++;
  180. }
  181. entry = entry->prev;
  182. }
  183. return match;
  184. }
  185. static unsigned int hash_value_early_read;
  186. static int early_resume_init(void)
  187. {
  188. hash_value_early_read = read_magic_time();
  189. return 0;
  190. }
  191. static int late_resume_init(void)
  192. {
  193. unsigned int val = hash_value_early_read;
  194. unsigned int user, file, dev;
  195. user = val % USERHASH;
  196. val = val / USERHASH;
  197. file = val % FILEHASH;
  198. val = val / FILEHASH;
  199. dev = val /* % DEVHASH */;
  200. printk(" Magic number: %d:%d:%d\n", user, file, dev);
  201. show_file_hash(file);
  202. show_dev_hash(dev);
  203. return 0;
  204. }
  205. core_initcall(early_resume_init);
  206. late_initcall(late_resume_init);