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

amd_fam14h_idle.c (8630B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  (C) 2010,2011      Thomas Renninger <trenn@suse.de>, Novell Inc.
      4 *
      5 *  PCI initialization based on example code from:
      6 *  Andreas Herrmann <andreas.herrmann3@amd.com>
      7 */
      8
      9#if defined(__i386__) || defined(__x86_64__)
     10
     11#include <stdio.h>
     12#include <stdlib.h>
     13#include <stdint.h>
     14#include <time.h>
     15#include <string.h>
     16
     17#include <pci/pci.h>
     18
     19#include "idle_monitor/cpupower-monitor.h"
     20#include "helpers/helpers.h"
     21
     22#define PCI_NON_PC0_OFFSET	0xb0
     23#define PCI_PC1_OFFSET		0xb4
     24#define PCI_PC6_OFFSET		0xb8
     25
     26#define PCI_MONITOR_ENABLE_REG  0xe0
     27
     28#define PCI_NON_PC0_ENABLE_BIT	0
     29#define PCI_PC1_ENABLE_BIT	1
     30#define PCI_PC6_ENABLE_BIT	2
     31
     32#define PCI_NBP1_STAT_OFFSET	0x98
     33#define PCI_NBP1_ACTIVE_BIT	2
     34#define PCI_NBP1_ENTERED_BIT	1
     35
     36#define PCI_NBP1_CAP_OFFSET	0x90
     37#define PCI_NBP1_CAPABLE_BIT    31
     38
     39#define OVERFLOW_MS		343597 /* 32 bit register filled at 12500 HZ
     40					  (1 tick per 80ns) */
     41
     42enum amd_fam14h_states {NON_PC0 = 0, PC1, PC6, NBP1,
     43			AMD_FAM14H_STATE_NUM};
     44
     45static int fam14h_get_count_percent(unsigned int self_id, double *percent,
     46				    unsigned int cpu);
     47static int fam14h_nbp1_count(unsigned int id, unsigned long long *count,
     48			     unsigned int cpu);
     49
     50static cstate_t amd_fam14h_cstates[AMD_FAM14H_STATE_NUM] = {
     51	{
     52		.name			= "!PC0",
     53		.desc			= N_("Package in sleep state (PC1 or deeper)"),
     54		.id			= NON_PC0,
     55		.range			= RANGE_PACKAGE,
     56		.get_count_percent	= fam14h_get_count_percent,
     57	},
     58	{
     59		.name			= "PC1",
     60		.desc			= N_("Processor Package C1"),
     61		.id			= PC1,
     62		.range			= RANGE_PACKAGE,
     63		.get_count_percent	= fam14h_get_count_percent,
     64	},
     65	{
     66		.name			= "PC6",
     67		.desc			= N_("Processor Package C6"),
     68		.id			= PC6,
     69		.range			= RANGE_PACKAGE,
     70		.get_count_percent	= fam14h_get_count_percent,
     71	},
     72	{
     73		.name			= "NBP1",
     74		.desc			= N_("North Bridge P1 boolean counter (returns 0 or 1)"),
     75		.id			= NBP1,
     76		.range			= RANGE_PACKAGE,
     77		.get_count		= fam14h_nbp1_count,
     78	},
     79};
     80
     81static struct pci_access *pci_acc;
     82static struct pci_dev *amd_fam14h_pci_dev;
     83static int nbp1_entered;
     84
     85static struct timespec start_time;
     86static unsigned long long timediff;
     87
     88#ifdef DEBUG
     89struct timespec dbg_time;
     90long dbg_timediff;
     91#endif
     92
     93static unsigned long long *previous_count[AMD_FAM14H_STATE_NUM];
     94static unsigned long long *current_count[AMD_FAM14H_STATE_NUM];
     95
     96static int amd_fam14h_get_pci_info(struct cstate *state,
     97				   unsigned int *pci_offset,
     98				   unsigned int *enable_bit,
     99				   unsigned int cpu)
    100{
    101	switch (state->id) {
    102	case NON_PC0:
    103		*enable_bit = PCI_NON_PC0_ENABLE_BIT;
    104		*pci_offset = PCI_NON_PC0_OFFSET;
    105		break;
    106	case PC1:
    107		*enable_bit = PCI_PC1_ENABLE_BIT;
    108		*pci_offset = PCI_PC1_OFFSET;
    109		break;
    110	case PC6:
    111		*enable_bit = PCI_PC6_ENABLE_BIT;
    112		*pci_offset = PCI_PC6_OFFSET;
    113		break;
    114	case NBP1:
    115		*enable_bit = PCI_NBP1_ENTERED_BIT;
    116		*pci_offset = PCI_NBP1_STAT_OFFSET;
    117		break;
    118	default:
    119		return -1;
    120	}
    121	return 0;
    122}
    123
    124static int amd_fam14h_init(cstate_t *state, unsigned int cpu)
    125{
    126	int enable_bit, pci_offset, ret;
    127	uint32_t val;
    128
    129	ret = amd_fam14h_get_pci_info(state, &pci_offset, &enable_bit, cpu);
    130	if (ret)
    131		return ret;
    132
    133	/* NBP1 needs extra treating -> write 1 to D18F6x98 bit 1 for init */
    134	if (state->id == NBP1) {
    135		val = pci_read_long(amd_fam14h_pci_dev, pci_offset);
    136		val |= 1 << enable_bit;
    137		val = pci_write_long(amd_fam14h_pci_dev, pci_offset, val);
    138		return ret;
    139	}
    140
    141	/* Enable monitor */
    142	val = pci_read_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG);
    143	dprint("Init %s: read at offset: 0x%x val: %u\n", state->name,
    144	       PCI_MONITOR_ENABLE_REG, (unsigned int) val);
    145	val |= 1 << enable_bit;
    146	pci_write_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG, val);
    147
    148	dprint("Init %s: offset: 0x%x enable_bit: %d - val: %u (%u)\n",
    149	       state->name, PCI_MONITOR_ENABLE_REG, enable_bit,
    150	       (unsigned int) val, cpu);
    151
    152	/* Set counter to zero */
    153	pci_write_long(amd_fam14h_pci_dev, pci_offset, 0);
    154	previous_count[state->id][cpu] = 0;
    155
    156	return 0;
    157}
    158
    159static int amd_fam14h_disable(cstate_t *state, unsigned int cpu)
    160{
    161	int enable_bit, pci_offset, ret;
    162	uint32_t val;
    163
    164	ret = amd_fam14h_get_pci_info(state, &pci_offset, &enable_bit, cpu);
    165	if (ret)
    166		return ret;
    167
    168	val = pci_read_long(amd_fam14h_pci_dev, pci_offset);
    169	dprint("%s: offset: 0x%x %u\n", state->name, pci_offset, val);
    170	if (state->id == NBP1) {
    171		/* was the bit whether NBP1 got entered set? */
    172		nbp1_entered = (val & (1 << PCI_NBP1_ACTIVE_BIT)) |
    173			(val & (1 << PCI_NBP1_ENTERED_BIT));
    174
    175		dprint("NBP1 was %sentered - 0x%x - enable_bit: "
    176		       "%d - pci_offset: 0x%x\n",
    177		       nbp1_entered ? "" : "not ",
    178		       val, enable_bit, pci_offset);
    179		return ret;
    180	}
    181	current_count[state->id][cpu] = val;
    182
    183	dprint("%s: Current -  %llu (%u)\n", state->name,
    184	       current_count[state->id][cpu], cpu);
    185	dprint("%s: Previous - %llu (%u)\n", state->name,
    186	       previous_count[state->id][cpu], cpu);
    187
    188	val = pci_read_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG);
    189	val &= ~(1 << enable_bit);
    190	pci_write_long(amd_fam14h_pci_dev, PCI_MONITOR_ENABLE_REG, val);
    191
    192	return 0;
    193}
    194
    195static int fam14h_nbp1_count(unsigned int id, unsigned long long *count,
    196			     unsigned int cpu)
    197{
    198	if (id == NBP1) {
    199		if (nbp1_entered)
    200			*count = 1;
    201		else
    202			*count = 0;
    203		return 0;
    204	}
    205	return -1;
    206}
    207static int fam14h_get_count_percent(unsigned int id, double *percent,
    208				    unsigned int cpu)
    209{
    210	unsigned long diff;
    211
    212	if (id >= AMD_FAM14H_STATE_NUM)
    213		return -1;
    214	/* residency count in 80ns -> divide through 12.5 to get us residency */
    215	diff = current_count[id][cpu] - previous_count[id][cpu];
    216
    217	if (timediff == 0)
    218		*percent = 0.0;
    219	else
    220		*percent = 100.0 * diff / timediff / 12.5;
    221
    222	dprint("Timediff: %llu - res~: %lu us - percent: %.2f %%\n",
    223	       timediff, diff * 10 / 125, *percent);
    224
    225	return 0;
    226}
    227
    228static int amd_fam14h_start(void)
    229{
    230	int num, cpu;
    231	clock_gettime(CLOCK_REALTIME, &start_time);
    232	for (num = 0; num < AMD_FAM14H_STATE_NUM; num++) {
    233		for (cpu = 0; cpu < cpu_count; cpu++)
    234			amd_fam14h_init(&amd_fam14h_cstates[num], cpu);
    235	}
    236#ifdef DEBUG
    237	clock_gettime(CLOCK_REALTIME, &dbg_time);
    238	dbg_timediff = timespec_diff_us(start_time, dbg_time);
    239	dprint("Enabling counters took: %lu us\n",
    240	       dbg_timediff);
    241#endif
    242	return 0;
    243}
    244
    245static int amd_fam14h_stop(void)
    246{
    247	int num, cpu;
    248	struct timespec end_time;
    249
    250	clock_gettime(CLOCK_REALTIME, &end_time);
    251
    252	for (num = 0; num < AMD_FAM14H_STATE_NUM; num++) {
    253		for (cpu = 0; cpu < cpu_count; cpu++)
    254			amd_fam14h_disable(&amd_fam14h_cstates[num], cpu);
    255	}
    256#ifdef DEBUG
    257	clock_gettime(CLOCK_REALTIME, &dbg_time);
    258	dbg_timediff = timespec_diff_us(end_time, dbg_time);
    259	dprint("Disabling counters took: %lu ns\n", dbg_timediff);
    260#endif
    261	timediff = timespec_diff_us(start_time, end_time);
    262	if (timediff / 1000 > OVERFLOW_MS)
    263		print_overflow_err((unsigned int)timediff / 1000000,
    264				   OVERFLOW_MS / 1000);
    265
    266	return 0;
    267}
    268
    269static int is_nbp1_capable(void)
    270{
    271	uint32_t val;
    272	val = pci_read_long(amd_fam14h_pci_dev, PCI_NBP1_CAP_OFFSET);
    273	return val & (1 << 31);
    274}
    275
    276struct cpuidle_monitor *amd_fam14h_register(void)
    277{
    278	int num;
    279
    280	if (cpupower_cpu_info.vendor != X86_VENDOR_AMD)
    281		return NULL;
    282
    283	if (cpupower_cpu_info.family == 0x14)
    284		strncpy(amd_fam14h_monitor.name, "Fam_14h",
    285			MONITOR_NAME_LEN - 1);
    286	else if (cpupower_cpu_info.family == 0x12)
    287		strncpy(amd_fam14h_monitor.name, "Fam_12h",
    288			MONITOR_NAME_LEN - 1);
    289	else
    290		return NULL;
    291
    292	/* We do not alloc for nbp1 machine wide counter */
    293	for (num = 0; num < AMD_FAM14H_STATE_NUM - 1; num++) {
    294		previous_count[num] = calloc(cpu_count,
    295					      sizeof(unsigned long long));
    296		current_count[num]  = calloc(cpu_count,
    297					      sizeof(unsigned long long));
    298	}
    299
    300	/* We need PCI device: Slot 18, Func 6, compare with BKDG
    301	   for fam 12h/14h */
    302	amd_fam14h_pci_dev = pci_slot_func_init(&pci_acc, 0x18, 6);
    303	if (amd_fam14h_pci_dev == NULL || pci_acc == NULL)
    304		return NULL;
    305
    306	if (!is_nbp1_capable())
    307		amd_fam14h_monitor.hw_states_num = AMD_FAM14H_STATE_NUM - 1;
    308
    309	amd_fam14h_monitor.name_len = strlen(amd_fam14h_monitor.name);
    310	return &amd_fam14h_monitor;
    311}
    312
    313static void amd_fam14h_unregister(void)
    314{
    315	int num;
    316	for (num = 0; num < AMD_FAM14H_STATE_NUM - 1; num++) {
    317		free(previous_count[num]);
    318		free(current_count[num]);
    319	}
    320	pci_cleanup(pci_acc);
    321}
    322
    323struct cpuidle_monitor amd_fam14h_monitor = {
    324	.name			= "",
    325	.hw_states		= amd_fam14h_cstates,
    326	.hw_states_num		= AMD_FAM14H_STATE_NUM,
    327	.start			= amd_fam14h_start,
    328	.stop			= amd_fam14h_stop,
    329	.do_register		= amd_fam14h_register,
    330	.unregister		= amd_fam14h_unregister,
    331	.flags.needs_root	= 1,
    332	.overflow_s		= OVERFLOW_MS / 1000,
    333};
    334#endif /* #if defined(__i386__) || defined(__x86_64__) */