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

opal-xscom.c (4525B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * PowerNV SCOM bus debugfs interface
      4 *
      5 * Copyright 2010 Benjamin Herrenschmidt, IBM Corp
      6 *                <benh@kernel.crashing.org>
      7 *     and        David Gibson, IBM Corporation.
      8 * Copyright 2013 IBM Corp.
      9 */
     10
     11#include <linux/kernel.h>
     12#include <linux/of.h>
     13#include <linux/bug.h>
     14#include <linux/gfp.h>
     15#include <linux/slab.h>
     16#include <linux/uaccess.h>
     17#include <linux/debugfs.h>
     18
     19#include <asm/machdep.h>
     20#include <asm/firmware.h>
     21#include <asm/opal.h>
     22#include <asm/prom.h>
     23
     24static u64 opal_scom_unmangle(u64 addr)
     25{
     26	u64 tmp;
     27
     28	/*
     29	 * XSCOM addresses use the top nibble to set indirect mode and
     30	 * its form.  Bits 4-11 are always 0.
     31	 *
     32	 * Because the debugfs interface uses signed offsets and shifts
     33	 * the address left by 3, we basically cannot use the top 4 bits
     34	 * of the 64-bit address, and thus cannot use the indirect bit.
     35	 *
     36	 * To deal with that, we support the indirect bits being in
     37	 * bits 4-7 (IBM notation) instead of bit 0-3 in this API, we
     38	 * do the conversion here.
     39	 *
     40	 * For in-kernel use, we don't need to do this mangling.  In
     41	 * kernel won't have bits 4-7 set.
     42	 *
     43	 * So:
     44	 *   debugfs will always   set 0-3 = 0 and clear 4-7
     45	 *    kernel will always clear 0-3 = 0 and   set 4-7
     46	 */
     47	tmp = addr;
     48	tmp  &= 0x0f00000000000000;
     49	addr &= 0xf0ffffffffffffff;
     50	addr |= tmp << 4;
     51
     52	return addr;
     53}
     54
     55static int opal_scom_read(uint32_t chip, uint64_t addr, u64 reg, u64 *value)
     56{
     57	int64_t rc;
     58	__be64 v;
     59
     60	reg = opal_scom_unmangle(addr + reg);
     61	rc = opal_xscom_read(chip, reg, (__be64 *)__pa(&v));
     62	if (rc) {
     63		*value = 0xfffffffffffffffful;
     64		return -EIO;
     65	}
     66	*value = be64_to_cpu(v);
     67	return 0;
     68}
     69
     70static int opal_scom_write(uint32_t chip, uint64_t addr, u64 reg, u64 value)
     71{
     72	int64_t rc;
     73
     74	reg = opal_scom_unmangle(addr + reg);
     75	rc = opal_xscom_write(chip, reg, value);
     76	if (rc)
     77		return -EIO;
     78	return 0;
     79}
     80
     81struct scom_debug_entry {
     82	u32 chip;
     83	struct debugfs_blob_wrapper path;
     84	char name[16];
     85};
     86
     87static ssize_t scom_debug_read(struct file *filp, char __user *ubuf,
     88			       size_t count, loff_t *ppos)
     89{
     90	struct scom_debug_entry *ent = filp->private_data;
     91	u64 __user *ubuf64 = (u64 __user *)ubuf;
     92	loff_t off = *ppos;
     93	ssize_t done = 0;
     94	u64 reg, reg_base, reg_cnt, val;
     95	int rc;
     96
     97	if (off < 0 || (off & 7) || (count & 7))
     98		return -EINVAL;
     99	reg_base = off >> 3;
    100	reg_cnt = count >> 3;
    101
    102	for (reg = 0; reg < reg_cnt; reg++) {
    103		rc = opal_scom_read(ent->chip, reg_base, reg, &val);
    104		if (!rc)
    105			rc = put_user(val, ubuf64);
    106		if (rc) {
    107			if (!done)
    108				done = rc;
    109			break;
    110		}
    111		ubuf64++;
    112		*ppos += 8;
    113		done += 8;
    114	}
    115	return done;
    116}
    117
    118static ssize_t scom_debug_write(struct file *filp, const char __user *ubuf,
    119				size_t count, loff_t *ppos)
    120{
    121	struct scom_debug_entry *ent = filp->private_data;
    122	u64 __user *ubuf64 = (u64 __user *)ubuf;
    123	loff_t off = *ppos;
    124	ssize_t done = 0;
    125	u64 reg, reg_base, reg_cnt, val;
    126	int rc;
    127
    128	if (off < 0 || (off & 7) || (count & 7))
    129		return -EINVAL;
    130	reg_base = off >> 3;
    131	reg_cnt = count >> 3;
    132
    133	for (reg = 0; reg < reg_cnt; reg++) {
    134		rc = get_user(val, ubuf64);
    135		if (!rc)
    136			rc = opal_scom_write(ent->chip, reg_base, reg,  val);
    137		if (rc) {
    138			if (!done)
    139				done = rc;
    140			break;
    141		}
    142		ubuf64++;
    143		done += 8;
    144	}
    145	return done;
    146}
    147
    148static const struct file_operations scom_debug_fops = {
    149	.read =		scom_debug_read,
    150	.write =	scom_debug_write,
    151	.open =		simple_open,
    152	.llseek =	default_llseek,
    153};
    154
    155static int scom_debug_init_one(struct dentry *root, struct device_node *dn,
    156			       int chip)
    157{
    158	struct scom_debug_entry *ent;
    159	struct dentry *dir;
    160
    161	ent = kzalloc(sizeof(*ent), GFP_KERNEL);
    162	if (!ent)
    163		return -ENOMEM;
    164
    165	ent->chip = chip;
    166	snprintf(ent->name, 16, "%08x", chip);
    167	ent->path.data = (void *)kasprintf(GFP_KERNEL, "%pOF", dn);
    168	ent->path.size = strlen((char *)ent->path.data);
    169
    170	dir = debugfs_create_dir(ent->name, root);
    171	if (!dir) {
    172		kfree(ent->path.data);
    173		kfree(ent);
    174		return -1;
    175	}
    176
    177	debugfs_create_blob("devspec", 0400, dir, &ent->path);
    178	debugfs_create_file("access", 0600, dir, ent, &scom_debug_fops);
    179
    180	return 0;
    181}
    182
    183static int scom_debug_init(void)
    184{
    185	struct device_node *dn;
    186	struct dentry *root;
    187	int chip, rc;
    188
    189	if (!firmware_has_feature(FW_FEATURE_OPAL))
    190		return 0;
    191
    192	root = debugfs_create_dir("scom", arch_debugfs_dir);
    193	if (!root)
    194		return -1;
    195
    196	rc = 0;
    197	for_each_node_with_property(dn, "scom-controller") {
    198		chip = of_get_ibm_chip_id(dn);
    199		WARN_ON(chip == -1);
    200		rc |= scom_debug_init_one(root, dn, chip);
    201	}
    202
    203	return rc;
    204}
    205device_initcall(scom_debug_init);