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

drm_privacy_screen.c (14278B)


      1// SPDX-License-Identifier: MIT
      2/*
      3 * Copyright (C) 2020 - 2021 Red Hat, Inc.
      4 *
      5 * Authors:
      6 * Hans de Goede <hdegoede@redhat.com>
      7 */
      8
      9#include <linux/device.h>
     10#include <linux/kernel.h>
     11#include <linux/list.h>
     12#include <linux/module.h>
     13#include <linux/mutex.h>
     14#include <linux/slab.h>
     15#include <drm/drm_privacy_screen_machine.h>
     16#include <drm/drm_privacy_screen_consumer.h>
     17#include <drm/drm_privacy_screen_driver.h>
     18#include "drm_internal.h"
     19
     20/**
     21 * DOC: overview
     22 *
     23 * This class allows non KMS drivers, from e.g. drivers/platform/x86 to
     24 * register a privacy-screen device, which the KMS drivers can then use
     25 * to implement the standard privacy-screen properties, see
     26 * :ref:`Standard Connector Properties<standard_connector_properties>`.
     27 *
     28 * KMS drivers using a privacy-screen class device are advised to use the
     29 * drm_connector_attach_privacy_screen_provider() and
     30 * drm_connector_update_privacy_screen() helpers for dealing with this.
     31 */
     32
     33#define to_drm_privacy_screen(dev) \
     34	container_of(dev, struct drm_privacy_screen, dev)
     35
     36static DEFINE_MUTEX(drm_privacy_screen_lookup_lock);
     37static LIST_HEAD(drm_privacy_screen_lookup_list);
     38
     39static DEFINE_MUTEX(drm_privacy_screen_devs_lock);
     40static LIST_HEAD(drm_privacy_screen_devs);
     41
     42/*** drm_privacy_screen_machine.h functions ***/
     43
     44/**
     45 * drm_privacy_screen_lookup_add - add an entry to the static privacy-screen
     46 *    lookup list
     47 * @lookup: lookup list entry to add
     48 *
     49 * Add an entry to the static privacy-screen lookup list. Note the
     50 * &struct list_head which is part of the &struct drm_privacy_screen_lookup
     51 * gets added to a list owned by the privacy-screen core. So the passed in
     52 * &struct drm_privacy_screen_lookup must not be free-ed until it is removed
     53 * from the lookup list by calling drm_privacy_screen_lookup_remove().
     54 */
     55void drm_privacy_screen_lookup_add(struct drm_privacy_screen_lookup *lookup)
     56{
     57	mutex_lock(&drm_privacy_screen_lookup_lock);
     58	list_add(&lookup->list, &drm_privacy_screen_lookup_list);
     59	mutex_unlock(&drm_privacy_screen_lookup_lock);
     60}
     61EXPORT_SYMBOL(drm_privacy_screen_lookup_add);
     62
     63/**
     64 * drm_privacy_screen_lookup_remove - remove an entry to the static
     65 *    privacy-screen lookup list
     66 * @lookup: lookup list entry to remove
     67 *
     68 * Remove an entry previously added with drm_privacy_screen_lookup_add()
     69 * from the static privacy-screen lookup list.
     70 */
     71void drm_privacy_screen_lookup_remove(struct drm_privacy_screen_lookup *lookup)
     72{
     73	mutex_lock(&drm_privacy_screen_lookup_lock);
     74	list_del(&lookup->list);
     75	mutex_unlock(&drm_privacy_screen_lookup_lock);
     76}
     77EXPORT_SYMBOL(drm_privacy_screen_lookup_remove);
     78
     79/*** drm_privacy_screen_consumer.h functions ***/
     80
     81static struct drm_privacy_screen *drm_privacy_screen_get_by_name(
     82	const char *name)
     83{
     84	struct drm_privacy_screen *priv;
     85	struct device *dev = NULL;
     86
     87	mutex_lock(&drm_privacy_screen_devs_lock);
     88
     89	list_for_each_entry(priv, &drm_privacy_screen_devs, list) {
     90		if (strcmp(dev_name(&priv->dev), name) == 0) {
     91			dev = get_device(&priv->dev);
     92			break;
     93		}
     94	}
     95
     96	mutex_unlock(&drm_privacy_screen_devs_lock);
     97
     98	return dev ? to_drm_privacy_screen(dev) : NULL;
     99}
    100
    101/**
    102 * drm_privacy_screen_get - get a privacy-screen provider
    103 * @dev: consumer-device for which to get a privacy-screen provider
    104 * @con_id: (video)connector name for which to get a privacy-screen provider
    105 *
    106 * Get a privacy-screen provider for a privacy-screen attached to the
    107 * display described by the @dev and @con_id parameters.
    108 *
    109 * Return:
    110 * * A pointer to a &struct drm_privacy_screen on success.
    111 * * ERR_PTR(-ENODEV) if no matching privacy-screen is found
    112 * * ERR_PTR(-EPROBE_DEFER) if there is a matching privacy-screen,
    113 *                          but it has not been registered yet.
    114 */
    115struct drm_privacy_screen *drm_privacy_screen_get(struct device *dev,
    116						  const char *con_id)
    117{
    118	const char *dev_id = dev ? dev_name(dev) : NULL;
    119	struct drm_privacy_screen_lookup *l;
    120	struct drm_privacy_screen *priv;
    121	const char *provider = NULL;
    122	int match, best = -1;
    123
    124	/*
    125	 * For now we only support using a static lookup table, which is
    126	 * populated by the drm_privacy_screen_arch_init() call. This should
    127	 * be extended with device-tree / fw_node lookup when support is added
    128	 * for device-tree using hardware with a privacy-screen.
    129	 *
    130	 * The lookup algorithm was shamelessly taken from the clock
    131	 * framework:
    132	 *
    133	 * We do slightly fuzzy matching here:
    134	 *  An entry with a NULL ID is assumed to be a wildcard.
    135	 *  If an entry has a device ID, it must match
    136	 *  If an entry has a connection ID, it must match
    137	 * Then we take the most specific entry - with the following order
    138	 * of precedence: dev+con > dev only > con only.
    139	 */
    140	mutex_lock(&drm_privacy_screen_lookup_lock);
    141
    142	list_for_each_entry(l, &drm_privacy_screen_lookup_list, list) {
    143		match = 0;
    144
    145		if (l->dev_id) {
    146			if (!dev_id || strcmp(l->dev_id, dev_id))
    147				continue;
    148
    149			match += 2;
    150		}
    151
    152		if (l->con_id) {
    153			if (!con_id || strcmp(l->con_id, con_id))
    154				continue;
    155
    156			match += 1;
    157		}
    158
    159		if (match > best) {
    160			provider = l->provider;
    161			best = match;
    162		}
    163	}
    164
    165	mutex_unlock(&drm_privacy_screen_lookup_lock);
    166
    167	if (!provider)
    168		return ERR_PTR(-ENODEV);
    169
    170	priv = drm_privacy_screen_get_by_name(provider);
    171	if (!priv)
    172		return ERR_PTR(-EPROBE_DEFER);
    173
    174	return priv;
    175}
    176EXPORT_SYMBOL(drm_privacy_screen_get);
    177
    178/**
    179 * drm_privacy_screen_put - release a privacy-screen reference
    180 * @priv: privacy screen reference to release
    181 *
    182 * Release a privacy-screen provider reference gotten through
    183 * drm_privacy_screen_get(). May be called with a NULL or ERR_PTR,
    184 * in which case it is a no-op.
    185 */
    186void drm_privacy_screen_put(struct drm_privacy_screen *priv)
    187{
    188	if (IS_ERR_OR_NULL(priv))
    189		return;
    190
    191	put_device(&priv->dev);
    192}
    193EXPORT_SYMBOL(drm_privacy_screen_put);
    194
    195/**
    196 * drm_privacy_screen_set_sw_state - set a privacy-screen's sw-state
    197 * @priv: privacy screen to set the sw-state for
    198 * @sw_state: new sw-state value to set
    199 *
    200 * Set the sw-state of a privacy screen. If the privacy-screen is not
    201 * in a locked hw-state, then the actual and hw-state of the privacy-screen
    202 * will be immediately updated to the new value. If the privacy-screen is
    203 * in a locked hw-state, then the new sw-state will be remembered as the
    204 * requested state to put the privacy-screen in when it becomes unlocked.
    205 *
    206 * Return: 0 on success, negative error code on failure.
    207 */
    208int drm_privacy_screen_set_sw_state(struct drm_privacy_screen *priv,
    209				    enum drm_privacy_screen_status sw_state)
    210{
    211	int ret = 0;
    212
    213	mutex_lock(&priv->lock);
    214
    215	if (!priv->ops) {
    216		ret = -ENODEV;
    217		goto out;
    218	}
    219
    220	/*
    221	 * As per the DRM connector properties documentation, setting the
    222	 * sw_state while the hw_state is locked is allowed. In this case
    223	 * it is a no-op other then storing the new sw_state so that it
    224	 * can be honored when the state gets unlocked.
    225	 * Also skip the set if the hw already is in the desired state.
    226	 */
    227	if (priv->hw_state >= PRIVACY_SCREEN_DISABLED_LOCKED ||
    228	    priv->hw_state == sw_state) {
    229		priv->sw_state = sw_state;
    230		goto out;
    231	}
    232
    233	ret = priv->ops->set_sw_state(priv, sw_state);
    234out:
    235	mutex_unlock(&priv->lock);
    236	return ret;
    237}
    238EXPORT_SYMBOL(drm_privacy_screen_set_sw_state);
    239
    240/**
    241 * drm_privacy_screen_get_state - get privacy-screen's current state
    242 * @priv: privacy screen to get the state for
    243 * @sw_state_ret: address where to store the privacy-screens current sw-state
    244 * @hw_state_ret: address where to store the privacy-screens current hw-state
    245 *
    246 * Get the current state of a privacy-screen, both the sw-state and the
    247 * hw-state.
    248 */
    249void drm_privacy_screen_get_state(struct drm_privacy_screen *priv,
    250				  enum drm_privacy_screen_status *sw_state_ret,
    251				  enum drm_privacy_screen_status *hw_state_ret)
    252{
    253	mutex_lock(&priv->lock);
    254	*sw_state_ret = priv->sw_state;
    255	*hw_state_ret = priv->hw_state;
    256	mutex_unlock(&priv->lock);
    257}
    258EXPORT_SYMBOL(drm_privacy_screen_get_state);
    259
    260/**
    261 * drm_privacy_screen_register_notifier - register a notifier
    262 * @priv: Privacy screen to register the notifier with
    263 * @nb: Notifier-block for the notifier to register
    264 *
    265 * Register a notifier with the privacy-screen to be notified of changes made
    266 * to the privacy-screen state from outside of the privacy-screen class.
    267 * E.g. the state may be changed by the hardware itself in response to a
    268 * hotkey press.
    269 *
    270 * The notifier is called with no locks held. The new hw_state and sw_state
    271 * can be retrieved using the drm_privacy_screen_get_state() function.
    272 * A pointer to the drm_privacy_screen's struct is passed as the ``void *data``
    273 * argument of the notifier_block's notifier_call.
    274 *
    275 * The notifier will NOT be called when changes are made through
    276 * drm_privacy_screen_set_sw_state(). It is only called for external changes.
    277 *
    278 * Return: 0 on success, negative error code on failure.
    279 */
    280int drm_privacy_screen_register_notifier(struct drm_privacy_screen *priv,
    281					 struct notifier_block *nb)
    282{
    283	return blocking_notifier_chain_register(&priv->notifier_head, nb);
    284}
    285EXPORT_SYMBOL(drm_privacy_screen_register_notifier);
    286
    287/**
    288 * drm_privacy_screen_unregister_notifier - unregister a notifier
    289 * @priv: Privacy screen to register the notifier with
    290 * @nb: Notifier-block for the notifier to register
    291 *
    292 * Unregister a notifier registered with drm_privacy_screen_register_notifier().
    293 *
    294 * Return: 0 on success, negative error code on failure.
    295 */
    296int drm_privacy_screen_unregister_notifier(struct drm_privacy_screen *priv,
    297					   struct notifier_block *nb)
    298{
    299	return blocking_notifier_chain_unregister(&priv->notifier_head, nb);
    300}
    301EXPORT_SYMBOL(drm_privacy_screen_unregister_notifier);
    302
    303/*** drm_privacy_screen_driver.h functions ***/
    304
    305static ssize_t sw_state_show(struct device *dev,
    306			     struct device_attribute *attr, char *buf)
    307{
    308	struct drm_privacy_screen *priv = to_drm_privacy_screen(dev);
    309	const char * const sw_state_names[] = {
    310		"Disabled",
    311		"Enabled",
    312	};
    313	ssize_t ret;
    314
    315	mutex_lock(&priv->lock);
    316
    317	if (!priv->ops)
    318		ret = -ENODEV;
    319	else if (WARN_ON(priv->sw_state >= ARRAY_SIZE(sw_state_names)))
    320		ret = -ENXIO;
    321	else
    322		ret = sprintf(buf, "%s\n", sw_state_names[priv->sw_state]);
    323
    324	mutex_unlock(&priv->lock);
    325	return ret;
    326}
    327/*
    328 * RO: Do not allow setting the sw_state through sysfs, this MUST be done
    329 * through the drm_properties on the drm_connector.
    330 */
    331static DEVICE_ATTR_RO(sw_state);
    332
    333static ssize_t hw_state_show(struct device *dev,
    334			     struct device_attribute *attr, char *buf)
    335{
    336	struct drm_privacy_screen *priv = to_drm_privacy_screen(dev);
    337	const char * const hw_state_names[] = {
    338		"Disabled",
    339		"Enabled",
    340		"Disabled, locked",
    341		"Enabled, locked",
    342	};
    343	ssize_t ret;
    344
    345	mutex_lock(&priv->lock);
    346
    347	if (!priv->ops)
    348		ret = -ENODEV;
    349	else if (WARN_ON(priv->hw_state >= ARRAY_SIZE(hw_state_names)))
    350		ret = -ENXIO;
    351	else
    352		ret = sprintf(buf, "%s\n", hw_state_names[priv->hw_state]);
    353
    354	mutex_unlock(&priv->lock);
    355	return ret;
    356}
    357static DEVICE_ATTR_RO(hw_state);
    358
    359static struct attribute *drm_privacy_screen_attrs[] = {
    360	&dev_attr_sw_state.attr,
    361	&dev_attr_hw_state.attr,
    362	NULL
    363};
    364ATTRIBUTE_GROUPS(drm_privacy_screen);
    365
    366static struct device_type drm_privacy_screen_type = {
    367	.name = "privacy_screen",
    368	.groups = drm_privacy_screen_groups,
    369};
    370
    371static void drm_privacy_screen_device_release(struct device *dev)
    372{
    373	struct drm_privacy_screen *priv = to_drm_privacy_screen(dev);
    374
    375	kfree(priv);
    376}
    377
    378/**
    379 * drm_privacy_screen_register - register a privacy-screen
    380 * @parent: parent-device for the privacy-screen
    381 * @ops: &struct drm_privacy_screen_ops pointer with ops for the privacy-screen
    382 * @data: Private data owned by the privacy screen provider
    383 *
    384 * Create and register a privacy-screen.
    385 *
    386 * Return:
    387 * * A pointer to the created privacy-screen on success.
    388 * * An ERR_PTR(errno) on failure.
    389 */
    390struct drm_privacy_screen *drm_privacy_screen_register(
    391	struct device *parent, const struct drm_privacy_screen_ops *ops,
    392	void *data)
    393{
    394	struct drm_privacy_screen *priv;
    395	int ret;
    396
    397	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    398	if (!priv)
    399		return ERR_PTR(-ENOMEM);
    400
    401	mutex_init(&priv->lock);
    402	BLOCKING_INIT_NOTIFIER_HEAD(&priv->notifier_head);
    403
    404	priv->dev.class = drm_class;
    405	priv->dev.type = &drm_privacy_screen_type;
    406	priv->dev.parent = parent;
    407	priv->dev.release = drm_privacy_screen_device_release;
    408	dev_set_name(&priv->dev, "privacy_screen-%s", dev_name(parent));
    409	priv->drvdata = data;
    410	priv->ops = ops;
    411
    412	priv->ops->get_hw_state(priv);
    413
    414	ret = device_register(&priv->dev);
    415	if (ret) {
    416		put_device(&priv->dev);
    417		return ERR_PTR(ret);
    418	}
    419
    420	mutex_lock(&drm_privacy_screen_devs_lock);
    421	list_add(&priv->list, &drm_privacy_screen_devs);
    422	mutex_unlock(&drm_privacy_screen_devs_lock);
    423
    424	return priv;
    425}
    426EXPORT_SYMBOL(drm_privacy_screen_register);
    427
    428/**
    429 * drm_privacy_screen_unregister - unregister privacy-screen
    430 * @priv: privacy-screen to unregister
    431 *
    432 * Unregister a privacy-screen registered with drm_privacy_screen_register().
    433 * May be called with a NULL or ERR_PTR, in which case it is a no-op.
    434 */
    435void drm_privacy_screen_unregister(struct drm_privacy_screen *priv)
    436{
    437	if (IS_ERR_OR_NULL(priv))
    438		return;
    439
    440	mutex_lock(&drm_privacy_screen_devs_lock);
    441	list_del(&priv->list);
    442	mutex_unlock(&drm_privacy_screen_devs_lock);
    443
    444	mutex_lock(&priv->lock);
    445	priv->drvdata = NULL;
    446	priv->ops = NULL;
    447	mutex_unlock(&priv->lock);
    448
    449	device_unregister(&priv->dev);
    450}
    451EXPORT_SYMBOL(drm_privacy_screen_unregister);
    452
    453/**
    454 * drm_privacy_screen_call_notifier_chain - notify consumers of state change
    455 * @priv: Privacy screen to register the notifier with
    456 *
    457 * A privacy-screen provider driver can call this functions upon external
    458 * changes to the privacy-screen state. E.g. the state may be changed by the
    459 * hardware itself in response to a hotkey press.
    460 * This function must be called without holding the privacy-screen lock.
    461 * the driver must update sw_state and hw_state to reflect the new state before
    462 * calling this function.
    463 * The expected behavior from the driver upon receiving an external state
    464 * change event is: 1. Take the lock; 2. Update sw_state and hw_state;
    465 * 3. Release the lock. 4. Call drm_privacy_screen_call_notifier_chain().
    466 */
    467void drm_privacy_screen_call_notifier_chain(struct drm_privacy_screen *priv)
    468{
    469	blocking_notifier_call_chain(&priv->notifier_head, 0, priv);
    470}
    471EXPORT_SYMBOL(drm_privacy_screen_call_notifier_chain);