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

acpi_lpit.c (4348B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2
      3/*
      4 * acpi_lpit.c - LPIT table processing functions
      5 *
      6 * Copyright (C) 2017 Intel Corporation. All rights reserved.
      7 */
      8
      9#include <linux/cpu.h>
     10#include <linux/acpi.h>
     11#include <asm/msr.h>
     12#include <asm/tsc.h>
     13
     14struct lpit_residency_info {
     15	struct acpi_generic_address gaddr;
     16	u64 frequency;
     17	void __iomem *iomem_addr;
     18};
     19
     20/* Storage for an memory mapped and FFH based entries */
     21static struct lpit_residency_info residency_info_mem;
     22static struct lpit_residency_info residency_info_ffh;
     23
     24static int lpit_read_residency_counter_us(u64 *counter, bool io_mem)
     25{
     26	int err;
     27
     28	if (io_mem) {
     29		u64 count = 0;
     30		int error;
     31
     32		error = acpi_os_read_iomem(residency_info_mem.iomem_addr, &count,
     33					   residency_info_mem.gaddr.bit_width);
     34		if (error)
     35			return error;
     36
     37		*counter = div64_u64(count * 1000000ULL, residency_info_mem.frequency);
     38		return 0;
     39	}
     40
     41	err = rdmsrl_safe(residency_info_ffh.gaddr.address, counter);
     42	if (!err) {
     43		u64 mask = GENMASK_ULL(residency_info_ffh.gaddr.bit_offset +
     44				       residency_info_ffh.gaddr. bit_width - 1,
     45				       residency_info_ffh.gaddr.bit_offset);
     46
     47		*counter &= mask;
     48		*counter >>= residency_info_ffh.gaddr.bit_offset;
     49		*counter = div64_u64(*counter * 1000000ULL, residency_info_ffh.frequency);
     50		return 0;
     51	}
     52
     53	return -ENODATA;
     54}
     55
     56static ssize_t low_power_idle_system_residency_us_show(struct device *dev,
     57						       struct device_attribute *attr,
     58						       char *buf)
     59{
     60	u64 counter;
     61	int ret;
     62
     63	ret = lpit_read_residency_counter_us(&counter, true);
     64	if (ret)
     65		return ret;
     66
     67	return sprintf(buf, "%llu\n", counter);
     68}
     69static DEVICE_ATTR_RO(low_power_idle_system_residency_us);
     70
     71static ssize_t low_power_idle_cpu_residency_us_show(struct device *dev,
     72						    struct device_attribute *attr,
     73						    char *buf)
     74{
     75	u64 counter;
     76	int ret;
     77
     78	ret = lpit_read_residency_counter_us(&counter, false);
     79	if (ret)
     80		return ret;
     81
     82	return sprintf(buf, "%llu\n", counter);
     83}
     84static DEVICE_ATTR_RO(low_power_idle_cpu_residency_us);
     85
     86int lpit_read_residency_count_address(u64 *address)
     87{
     88	if (!residency_info_mem.gaddr.address)
     89		return -EINVAL;
     90
     91	*address = residency_info_mem.gaddr.address;
     92
     93	return 0;
     94}
     95EXPORT_SYMBOL_GPL(lpit_read_residency_count_address);
     96
     97static void lpit_update_residency(struct lpit_residency_info *info,
     98				 struct acpi_lpit_native *lpit_native)
     99{
    100	info->frequency = lpit_native->counter_frequency ?
    101				lpit_native->counter_frequency : tsc_khz * 1000;
    102	if (!info->frequency)
    103		info->frequency = 1;
    104
    105	info->gaddr = lpit_native->residency_counter;
    106	if (info->gaddr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
    107		info->iomem_addr = ioremap(info->gaddr.address,
    108						   info->gaddr.bit_width / 8);
    109		if (!info->iomem_addr)
    110			return;
    111
    112		if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
    113			return;
    114
    115		/* Silently fail, if cpuidle attribute group is not present */
    116		sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj,
    117					&dev_attr_low_power_idle_system_residency_us.attr,
    118					"cpuidle");
    119	} else if (info->gaddr.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) {
    120		if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
    121			return;
    122
    123		/* Silently fail, if cpuidle attribute group is not present */
    124		sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj,
    125					&dev_attr_low_power_idle_cpu_residency_us.attr,
    126					"cpuidle");
    127	}
    128}
    129
    130static void lpit_process(u64 begin, u64 end)
    131{
    132	while (begin + sizeof(struct acpi_lpit_native) <= end) {
    133		struct acpi_lpit_native *lpit_native = (struct acpi_lpit_native *)begin;
    134
    135		if (!lpit_native->header.type && !lpit_native->header.flags) {
    136			if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY &&
    137			    !residency_info_mem.gaddr.address) {
    138				lpit_update_residency(&residency_info_mem, lpit_native);
    139			} else if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE &&
    140				   !residency_info_ffh.gaddr.address) {
    141				lpit_update_residency(&residency_info_ffh, lpit_native);
    142			}
    143		}
    144		begin += lpit_native->header.length;
    145	}
    146}
    147
    148void acpi_init_lpit(void)
    149{
    150	acpi_status status;
    151	struct acpi_table_lpit *lpit;
    152
    153	status = acpi_get_table(ACPI_SIG_LPIT, 0, (struct acpi_table_header **)&lpit);
    154	if (ACPI_FAILURE(status))
    155		return;
    156
    157	lpit_process((u64)lpit + sizeof(*lpit),
    158		     (u64)lpit + lpit->header.length);
    159
    160	acpi_put_table((struct acpi_table_header *)lpit);
    161}