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

dt_idle_states.c (6447B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * DT idle states parsing code.
      4 *
      5 * Copyright (C) 2014 ARM Ltd.
      6 * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
      7 */
      8
      9#define pr_fmt(fmt) "DT idle-states: " fmt
     10
     11#include <linux/cpuidle.h>
     12#include <linux/cpumask.h>
     13#include <linux/errno.h>
     14#include <linux/kernel.h>
     15#include <linux/module.h>
     16#include <linux/of.h>
     17#include <linux/of_device.h>
     18
     19#include "dt_idle_states.h"
     20
     21static int init_state_node(struct cpuidle_state *idle_state,
     22			   const struct of_device_id *match_id,
     23			   struct device_node *state_node)
     24{
     25	int err;
     26	const char *desc;
     27
     28	/*
     29	 * CPUidle drivers are expected to initialize the const void *data
     30	 * pointer of the passed in struct of_device_id array to the idle
     31	 * state enter function.
     32	 */
     33	idle_state->enter = match_id->data;
     34	/*
     35	 * Since this is not a "coupled" state, it's safe to assume interrupts
     36	 * won't be enabled when it exits allowing the tick to be frozen
     37	 * safely. So enter() can be also enter_s2idle() callback.
     38	 */
     39	idle_state->enter_s2idle = match_id->data;
     40
     41	err = of_property_read_u32(state_node, "wakeup-latency-us",
     42				   &idle_state->exit_latency);
     43	if (err) {
     44		u32 entry_latency, exit_latency;
     45
     46		err = of_property_read_u32(state_node, "entry-latency-us",
     47					   &entry_latency);
     48		if (err) {
     49			pr_debug(" * %pOF missing entry-latency-us property\n",
     50				 state_node);
     51			return -EINVAL;
     52		}
     53
     54		err = of_property_read_u32(state_node, "exit-latency-us",
     55					   &exit_latency);
     56		if (err) {
     57			pr_debug(" * %pOF missing exit-latency-us property\n",
     58				 state_node);
     59			return -EINVAL;
     60		}
     61		/*
     62		 * If wakeup-latency-us is missing, default to entry+exit
     63		 * latencies as defined in idle states bindings
     64		 */
     65		idle_state->exit_latency = entry_latency + exit_latency;
     66	}
     67
     68	err = of_property_read_u32(state_node, "min-residency-us",
     69				   &idle_state->target_residency);
     70	if (err) {
     71		pr_debug(" * %pOF missing min-residency-us property\n",
     72			     state_node);
     73		return -EINVAL;
     74	}
     75
     76	err = of_property_read_string(state_node, "idle-state-name", &desc);
     77	if (err)
     78		desc = state_node->name;
     79
     80	idle_state->flags = 0;
     81	if (of_property_read_bool(state_node, "local-timer-stop"))
     82		idle_state->flags |= CPUIDLE_FLAG_TIMER_STOP;
     83	/*
     84	 * TODO:
     85	 *	replace with kstrdup and pointer assignment when name
     86	 *	and desc become string pointers
     87	 */
     88	strncpy(idle_state->name, state_node->name, CPUIDLE_NAME_LEN - 1);
     89	strncpy(idle_state->desc, desc, CPUIDLE_DESC_LEN - 1);
     90	return 0;
     91}
     92
     93/*
     94 * Check that the idle state is uniform across all CPUs in the CPUidle driver
     95 * cpumask
     96 */
     97static bool idle_state_valid(struct device_node *state_node, unsigned int idx,
     98			     const cpumask_t *cpumask)
     99{
    100	int cpu;
    101	struct device_node *cpu_node, *curr_state_node;
    102	bool valid = true;
    103
    104	/*
    105	 * Compare idle state phandles for index idx on all CPUs in the
    106	 * CPUidle driver cpumask. Start from next logical cpu following
    107	 * cpumask_first(cpumask) since that's the CPU state_node was
    108	 * retrieved from. If a mismatch is found bail out straight
    109	 * away since we certainly hit a firmware misconfiguration.
    110	 */
    111	for (cpu = cpumask_next(cpumask_first(cpumask), cpumask);
    112	     cpu < nr_cpu_ids; cpu = cpumask_next(cpu, cpumask)) {
    113		cpu_node = of_cpu_device_node_get(cpu);
    114		curr_state_node = of_get_cpu_state_node(cpu_node, idx);
    115		if (state_node != curr_state_node)
    116			valid = false;
    117
    118		of_node_put(curr_state_node);
    119		of_node_put(cpu_node);
    120		if (!valid)
    121			break;
    122	}
    123
    124	return valid;
    125}
    126
    127/**
    128 * dt_init_idle_driver() - Parse the DT idle states and initialize the
    129 *			   idle driver states array
    130 * @drv:	  Pointer to CPU idle driver to be initialized
    131 * @matches:	  Array of of_device_id match structures to search in for
    132 *		  compatible idle state nodes. The data pointer for each valid
    133 *		  struct of_device_id entry in the matches array must point to
    134 *		  a function with the following signature, that corresponds to
    135 *		  the CPUidle state enter function signature:
    136 *
    137 *		  int (*)(struct cpuidle_device *dev,
    138 *			  struct cpuidle_driver *drv,
    139 *			  int index);
    140 *
    141 * @start_idx:    First idle state index to be initialized
    142 *
    143 * If DT idle states are detected and are valid the state count and states
    144 * array entries in the cpuidle driver are initialized accordingly starting
    145 * from index start_idx.
    146 *
    147 * Return: number of valid DT idle states parsed, <0 on failure
    148 */
    149int dt_init_idle_driver(struct cpuidle_driver *drv,
    150			const struct of_device_id *matches,
    151			unsigned int start_idx)
    152{
    153	struct cpuidle_state *idle_state;
    154	struct device_node *state_node, *cpu_node;
    155	const struct of_device_id *match_id;
    156	int i, err = 0;
    157	const cpumask_t *cpumask;
    158	unsigned int state_idx = start_idx;
    159
    160	if (state_idx >= CPUIDLE_STATE_MAX)
    161		return -EINVAL;
    162	/*
    163	 * We get the idle states for the first logical cpu in the
    164	 * driver mask (or cpu_possible_mask if the driver cpumask is not set)
    165	 * and we check through idle_state_valid() if they are uniform
    166	 * across CPUs, otherwise we hit a firmware misconfiguration.
    167	 */
    168	cpumask = drv->cpumask ? : cpu_possible_mask;
    169	cpu_node = of_cpu_device_node_get(cpumask_first(cpumask));
    170
    171	for (i = 0; ; i++) {
    172		state_node = of_get_cpu_state_node(cpu_node, i);
    173		if (!state_node)
    174			break;
    175
    176		match_id = of_match_node(matches, state_node);
    177		if (!match_id) {
    178			err = -ENODEV;
    179			break;
    180		}
    181
    182		if (!of_device_is_available(state_node)) {
    183			of_node_put(state_node);
    184			continue;
    185		}
    186
    187		if (!idle_state_valid(state_node, i, cpumask)) {
    188			pr_warn("%pOF idle state not valid, bailing out\n",
    189				state_node);
    190			err = -EINVAL;
    191			break;
    192		}
    193
    194		if (state_idx == CPUIDLE_STATE_MAX) {
    195			pr_warn("State index reached static CPU idle driver states array size\n");
    196			break;
    197		}
    198
    199		idle_state = &drv->states[state_idx++];
    200		err = init_state_node(idle_state, match_id, state_node);
    201		if (err) {
    202			pr_err("Parsing idle state node %pOF failed with err %d\n",
    203			       state_node, err);
    204			err = -EINVAL;
    205			break;
    206		}
    207		of_node_put(state_node);
    208	}
    209
    210	of_node_put(state_node);
    211	of_node_put(cpu_node);
    212	if (err)
    213		return err;
    214	/*
    215	 * Update the driver state count only if some valid DT idle states
    216	 * were detected
    217	 */
    218	if (i)
    219		drv->state_count = state_idx;
    220
    221	/*
    222	 * Return the number of present and valid DT idle states, which can
    223	 * also be 0 on platforms with missing DT idle states or legacy DT
    224	 * configuration predating the DT idle states bindings.
    225	 */
    226	return i;
    227}
    228EXPORT_SYMBOL_GPL(dt_init_idle_driver);