linux/drivers/base/power/trace.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   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
  81bool pm_trace_rtc_abused __read_mostly;
  82EXPORT_SYMBOL_GPL(pm_trace_rtc_abused);
  83
  84static unsigned int dev_hash_value;
  85
  86static 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        // June 7th, 2006
  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,    // June - counting from zero
  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
 118static unsigned int read_magic_time(void)
 119{
 120        struct rtc_time time;
 121        unsigned int val;
 122
 123        mc146818_get_time(&time);
 124        pr_info("RTC time: %ptRt, date: %ptRd\n", &time, &time);
 125        val = time.tm_year;                             /* 100 years */
 126        if (val > 100)
 127                val -= 100;
 128        val += time.tm_mon * 100;                       /* 12 months */
 129        val += (time.tm_mday-1) * 100 * 12;             /* 28 month-days */
 130        val += time.tm_hour * 100 * 12 * 28;            /* 24 hours */
 131        val += (time.tm_min / 3) * 100 * 12 * 28 * 24;  /* 20 3-minute intervals */
 132        return val;
 133}
 134
 135/*
 136 * This is just the sdbm hash function with a user-supplied
 137 * seed and final size parameter.
 138 */
 139static unsigned int hash_string(unsigned int seed, const char *data, unsigned int mod)
 140{
 141        unsigned char c;
 142        while ((c = *data++) != 0) {
 143                seed = (seed << 16) + (seed << 6) - seed + c;
 144        }
 145        return seed % mod;
 146}
 147
 148void set_trace_device(struct device *dev)
 149{
 150        dev_hash_value = hash_string(DEVSEED, dev_name(dev), DEVHASH);
 151}
 152EXPORT_SYMBOL(set_trace_device);
 153
 154/*
 155 * We could just take the "tracedata" index into the .tracedata
 156 * section instead. Generating a hash of the data gives us a
 157 * chance to work across kernel versions, and perhaps more
 158 * importantly it also gives us valid/invalid check (ie we will
 159 * likely not give totally bogus reports - if the hash matches,
 160 * it's not any guarantee, but it's a high _likelihood_ that
 161 * the match is valid).
 162 */
 163void generate_pm_trace(const void *tracedata, unsigned int user)
 164{
 165        unsigned short lineno = *(unsigned short *)tracedata;
 166        const char *file = *(const char **)(tracedata + 2);
 167        unsigned int user_hash_value, file_hash_value;
 168
 169        if (!x86_platform.legacy.rtc)
 170                return;
 171
 172        user_hash_value = user % USERHASH;
 173        file_hash_value = hash_string(lineno, file, FILEHASH);
 174        set_magic_time(user_hash_value, file_hash_value, dev_hash_value);
 175}
 176EXPORT_SYMBOL(generate_pm_trace);
 177
 178extern char __tracedata_start[], __tracedata_end[];
 179static int show_file_hash(unsigned int value)
 180{
 181        int match;
 182        char *tracedata;
 183
 184        match = 0;
 185        for (tracedata = __tracedata_start ; tracedata < __tracedata_end ;
 186                        tracedata += 2 + sizeof(unsigned long)) {
 187                unsigned short lineno = *(unsigned short *)tracedata;
 188                const char *file = *(const char **)(tracedata + 2);
 189                unsigned int hash = hash_string(lineno, file, FILEHASH);
 190                if (hash != value)
 191                        continue;
 192                pr_info("  hash matches %s:%u\n", file, lineno);
 193                match++;
 194        }
 195        return match;
 196}
 197
 198static int show_dev_hash(unsigned int value)
 199{
 200        int match = 0;
 201        struct list_head *entry;
 202
 203        device_pm_lock();
 204        entry = dpm_list.prev;
 205        while (entry != &dpm_list) {
 206                struct device * dev = to_device(entry);
 207                unsigned int hash = hash_string(DEVSEED, dev_name(dev), DEVHASH);
 208                if (hash == value) {
 209                        dev_info(dev, "hash matches\n");
 210                        match++;
 211                }
 212                entry = entry->prev;
 213        }
 214        device_pm_unlock();
 215        return match;
 216}
 217
 218static unsigned int hash_value_early_read;
 219
 220int show_trace_dev_match(char *buf, size_t size)
 221{
 222        unsigned int value = hash_value_early_read / (USERHASH * FILEHASH);
 223        int ret = 0;
 224        struct list_head *entry;
 225
 226        /*
 227         * It's possible that multiple devices will match the hash and we can't
 228         * tell which is the culprit, so it's best to output them all.
 229         */
 230        device_pm_lock();
 231        entry = dpm_list.prev;
 232        while (size && entry != &dpm_list) {
 233                struct device *dev = to_device(entry);
 234                unsigned int hash = hash_string(DEVSEED, dev_name(dev),
 235                                                DEVHASH);
 236                if (hash == value) {
 237                        int len = snprintf(buf, size, "%s\n",
 238                                            dev_driver_string(dev));
 239                        if (len > size)
 240                                len = size;
 241                        buf += len;
 242                        ret += len;
 243                        size -= len;
 244                }
 245                entry = entry->prev;
 246        }
 247        device_pm_unlock();
 248        return ret;
 249}
 250
 251static int
 252pm_trace_notify(struct notifier_block *nb, unsigned long mode, void *_unused)
 253{
 254        switch (mode) {
 255        case PM_POST_HIBERNATION:
 256        case PM_POST_SUSPEND:
 257                if (pm_trace_rtc_abused) {
 258                        pm_trace_rtc_abused = false;
 259                        pr_warn("Possible incorrect RTC due to pm_trace, please use 'ntpdate' or 'rdate' to reset it.\n");
 260                }
 261                break;
 262        default:
 263                break;
 264        }
 265        return 0;
 266}
 267
 268static struct notifier_block pm_trace_nb = {
 269        .notifier_call = pm_trace_notify,
 270};
 271
 272static int __init early_resume_init(void)
 273{
 274        if (!x86_platform.legacy.rtc)
 275                return 0;
 276
 277        hash_value_early_read = read_magic_time();
 278        register_pm_notifier(&pm_trace_nb);
 279        return 0;
 280}
 281
 282static int __init late_resume_init(void)
 283{
 284        unsigned int val = hash_value_early_read;
 285        unsigned int user, file, dev;
 286
 287        if (!x86_platform.legacy.rtc)
 288                return 0;
 289
 290        user = val % USERHASH;
 291        val = val / USERHASH;
 292        file = val % FILEHASH;
 293        val = val / FILEHASH;
 294        dev = val /* % DEVHASH */;
 295
 296        pr_info("  Magic number: %d:%d:%d\n", user, file, dev);
 297        show_file_hash(file);
 298        show_dev_hash(dev);
 299        return 0;
 300}
 301
 302core_initcall(early_resume_init);
 303late_initcall(late_resume_init);
 304