cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

trace.c (7973B)


      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	if (mc146818_get_time(&time) < 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;				/* 100 years */
    130	if (val > 100)
    131		val -= 100;
    132	val += time.tm_mon * 100;			/* 12 months */
    133	val += (time.tm_mday-1) * 100 * 12;		/* 28 month-days */
    134	val += time.tm_hour * 100 * 12 * 28;		/* 24 hours */
    135	val += (time.tm_min / 3) * 100 * 12 * 28 * 24;	/* 20 3-minute intervals */
    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 */
    143static 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
    152void set_trace_device(struct device *dev)
    153{
    154	dev_hash_value = hash_string(DEVSEED, dev_name(dev), DEVHASH);
    155}
    156EXPORT_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 */
    167void 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}
    180EXPORT_SYMBOL(generate_pm_trace);
    181
    182extern char __tracedata_start[], __tracedata_end[];
    183static 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
    202static 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
    222static unsigned int hash_value_early_read;
    223
    224int 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 = snprintf(buf, size, "%s\n",
    242					    dev_driver_string(dev));
    243			if (len > size)
    244				len = size;
    245			buf += len;
    246			ret += len;
    247			size -= len;
    248		}
    249		entry = entry->prev;
    250	}
    251	device_pm_unlock();
    252	return ret;
    253}
    254
    255static int
    256pm_trace_notify(struct notifier_block *nb, unsigned long mode, void *_unused)
    257{
    258	switch (mode) {
    259	case PM_POST_HIBERNATION:
    260	case PM_POST_SUSPEND:
    261		if (pm_trace_rtc_abused) {
    262			pm_trace_rtc_abused = false;
    263			pr_warn("Possible incorrect RTC due to pm_trace, please use 'ntpdate' or 'rdate' to reset it.\n");
    264		}
    265		break;
    266	default:
    267		break;
    268	}
    269	return 0;
    270}
    271
    272static struct notifier_block pm_trace_nb = {
    273	.notifier_call = pm_trace_notify,
    274};
    275
    276static int __init early_resume_init(void)
    277{
    278	if (!x86_platform.legacy.rtc)
    279		return 0;
    280
    281	hash_value_early_read = read_magic_time();
    282	register_pm_notifier(&pm_trace_nb);
    283	return 0;
    284}
    285
    286static int __init late_resume_init(void)
    287{
    288	unsigned int val = hash_value_early_read;
    289	unsigned int user, file, dev;
    290
    291	if (!x86_platform.legacy.rtc)
    292		return 0;
    293
    294	user = val % USERHASH;
    295	val = val / USERHASH;
    296	file = val % FILEHASH;
    297	val = val / FILEHASH;
    298	dev = val /* % DEVHASH */;
    299
    300	pr_info("  Magic number: %d:%d:%d\n", user, file, dev);
    301	show_file_hash(file);
    302	show_dev_hash(dev);
    303	return 0;
    304}
    305
    306core_initcall(early_resume_init);
    307late_initcall(late_resume_init);