Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * drivers/base/power/trace.c
0004  *
0005  * Copyright (C) 2006 Linus Torvalds
0006  *
0007  * Trace facility for suspend/resume problems, when none of the
0008  * devices may be working.
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  * Horrid, horrid, horrid.
0024  *
0025  * It turns out that the _only_ piece of hardware that actually
0026  * keeps its value across a hard boot (and, more importantly, the
0027  * POST init sequence) is literally the realtime clock.
0028  *
0029  * Never mind that an RTC chip has 114 bytes (and often a whole
0030  * other bank of an additional 128 bytes) of nice SRAM that is
0031  * _designed_ to keep data - the POST will clear it. So we literally
0032  * can just use the few bytes of actual time data, which means that
0033  * we're really limited.
0034  *
0035  * It means, for example, that we can't use the seconds at all
0036  * (since the time between the hang and the boot might be more
0037  * than a minute), and we'd better not depend on the low bits of
0038  * the minutes either.
0039  *
0040  * There are the wday fields etc, but I wouldn't guarantee those
0041  * are dependable either. And if the date isn't valid, either the
0042  * hw or POST will do strange things.
0043  *
0044  * So we're left with:
0045  *  - year: 0-99
0046  *  - month: 0-11
0047  *  - day-of-month: 1-28
0048  *  - hour: 0-23
0049  *  - min: (0-30)*2
0050  *
0051  * Giving us a total range of 0-16128000 (0xf61800), ie less
0052  * than 24 bits of actual data we can save across reboots.
0053  *
0054  * And if your box can't boot in less than three minutes,
0055  * you're screwed.
0056  *
0057  * Now, almost 24 bits of data is pitifully small, so we need
0058  * to be pretty dense if we want to use it for anything nice.
0059  * What we do is that instead of saving off nice readable info,
0060  * we save off _hashes_ of information that we can hopefully
0061  * regenerate after the reboot.
0062  *
0063  * In particular, this means that we might be unlucky, and hit
0064  * a case where we have a hash collision, and we end up not
0065  * being able to tell for certain exactly which case happened.
0066  * But that's hopefully unlikely.
0067  *
0068  * What we do is to take the bits we can fit, and split them
0069  * into three parts (16*997*1009 = 16095568), and use the values
0070  * for:
0071  *  - 0-15: user-settable
0072  *  - 0-996: file + line number
0073  *  - 0-1008: device
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     // June 7th, 2006
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,    // June - counting from zero
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;             /* 100 years */
0130     if (val > 100)
0131         val -= 100;
0132     val += time.tm_mon * 100;           /* 12 months */
0133     val += (time.tm_mday-1) * 100 * 12;     /* 28 month-days */
0134     val += time.tm_hour * 100 * 12 * 28;        /* 24 hours */
0135     val += (time.tm_min / 3) * 100 * 12 * 28 * 24;  /* 20 3-minute intervals */
0136     return val;
0137 }
0138 
0139 /*
0140  * This is just the sdbm hash function with a user-supplied
0141  * seed and final size parameter.
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  * We could just take the "tracedata" index into the .tracedata
0160  * section instead. Generating a hash of the data gives us a
0161  * chance to work across kernel versions, and perhaps more
0162  * importantly it also gives us valid/invalid check (ie we will
0163  * likely not give totally bogus reports - if the hash matches,
0164  * it's not any guarantee, but it's a high _likelihood_ that
0165  * the match is valid).
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      * It's possible that multiple devices will match the hash and we can't
0232      * tell which is the culprit, so it's best to output them all.
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 /* % DEVHASH */;
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);