powerpc/scom: Improve debugfs interface

The current debugfs interface to scom is essentially unused
and racy. It uses two different files "address" and "data"
to perform accesses which is at best impractical for anything
but manual use by a developer.

This replaces it with an "access" file which represent the entire
scom address space which can be lseek/read/writen too.

This file only supports accesses that are 8 bytes aligned and
multiple of 8 bytes in size. The offset is logically the SCOM
address multiplied by 8.

Since nothing in userspace exploits that file at the moment, the ABI
change is a no-brainer.
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent d7a88c7e
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <asm/debug.h> #include <asm/debug.h>
#include <asm/prom.h> #include <asm/prom.h>
#include <asm/scom.h> #include <asm/scom.h>
#include <asm/uaccess.h>
const struct scom_controller *scom_controller; const struct scom_controller *scom_controller;
EXPORT_SYMBOL_GPL(scom_controller); EXPORT_SYMBOL_GPL(scom_controller);
...@@ -98,61 +99,89 @@ EXPORT_SYMBOL_GPL(scom_map_device); ...@@ -98,61 +99,89 @@ EXPORT_SYMBOL_GPL(scom_map_device);
#ifdef CONFIG_SCOM_DEBUGFS #ifdef CONFIG_SCOM_DEBUGFS
struct scom_debug_entry { struct scom_debug_entry {
struct device_node *dn; struct device_node *dn;
unsigned long addr; struct debugfs_blob_wrapper path;
scom_map_t map; char name[16];
spinlock_t lock;
char name[8];
struct debugfs_blob_wrapper blob;
}; };
static int scom_addr_set(void *data, u64 val) static ssize_t scom_debug_read(struct file *filp, char __user *ubuf,
{ size_t count, loff_t *ppos)
struct scom_debug_entry *ent = data;
ent->addr = 0;
scom_unmap(ent->map);
ent->map = scom_map(ent->dn, val, 1);
if (scom_map_ok(ent->map))
ent->addr = val;
else
return -EFAULT;
return 0;
}
static int scom_addr_get(void *data, u64 *val)
{ {
struct scom_debug_entry *ent = data; struct scom_debug_entry *ent = filp->private_data;
*val = ent->addr; u64 __user *ubuf64 = (u64 __user *)ubuf;
return 0; loff_t off = *ppos;
ssize_t done = 0;
u64 reg, reg_cnt, val;
scom_map_t map;
int rc;
if (off < 0 || (off & 7) || (count & 7))
return -EINVAL;
reg = off >> 3;
reg_cnt = count >> 3;
map = scom_map(ent->dn, reg, reg_cnt);
if (!scom_map_ok(map))
return -ENXIO;
for (reg = 0; reg < reg_cnt; reg++) {
rc = scom_read(map, reg, &val);
if (!rc)
rc = put_user(val, ubuf64);
if (rc) {
if (!done)
done = rc;
break;
}
ubuf64++;
*ppos += 8;
done += 8;
}
scom_unmap(map);
return done;
} }
DEFINE_SIMPLE_ATTRIBUTE(scom_addr_fops, scom_addr_get, scom_addr_set,
"0x%llx\n");
static int scom_val_set(void *data, u64 val) static ssize_t scom_debug_write(struct file* filp, const char __user *ubuf,
size_t count, loff_t *ppos)
{ {
struct scom_debug_entry *ent = data; struct scom_debug_entry *ent = filp->private_data;
u64 __user *ubuf64 = (u64 __user *)ubuf;
if (!scom_map_ok(ent->map)) loff_t off = *ppos;
return -EFAULT; ssize_t done = 0;
u64 reg, reg_cnt, val;
scom_write(ent->map, 0, val); scom_map_t map;
int rc;
return 0;
if (off < 0 || (off & 7) || (count & 7))
return -EINVAL;
reg = off >> 3;
reg_cnt = count >> 3;
map = scom_map(ent->dn, reg, reg_cnt);
if (!scom_map_ok(map))
return -ENXIO;
for (reg = 0; reg < reg_cnt; reg++) {
rc = get_user(val, ubuf64);
if (!rc)
rc = scom_write(map, reg, val);
if (rc) {
if (!done)
done = rc;
break;
}
ubuf64++;
done += 8;
}
scom_unmap(map);
return done;
} }
static int scom_val_get(void *data, u64 *val) static const struct file_operations scom_debug_fops = {
{ .read = scom_debug_read,
struct scom_debug_entry *ent = data; .write = scom_debug_write,
.open = simple_open,
if (!scom_map_ok(ent->map)) .llseek = default_llseek,
return -EFAULT; };
return scom_read(ent->map, 0, val);
}
DEFINE_SIMPLE_ATTRIBUTE(scom_val_fops, scom_val_get, scom_val_set,
"0x%llx\n");
static int scom_debug_init_one(struct dentry *root, struct device_node *dn, static int scom_debug_init_one(struct dentry *root, struct device_node *dn,
int i) int i)
...@@ -165,11 +194,9 @@ static int scom_debug_init_one(struct dentry *root, struct device_node *dn, ...@@ -165,11 +194,9 @@ static int scom_debug_init_one(struct dentry *root, struct device_node *dn,
return -ENOMEM; return -ENOMEM;
ent->dn = of_node_get(dn); ent->dn = of_node_get(dn);
ent->map = SCOM_MAP_INVALID; snprintf(ent->name, 16, "%08x", i);
spin_lock_init(&ent->lock); ent->path.data = (void*) dn->full_name;
snprintf(ent->name, 8, "scom%d", i); ent->path.size = strlen(dn->full_name);
ent->blob.data = (void*) dn->full_name;
ent->blob.size = strlen(dn->full_name);
dir = debugfs_create_dir(ent->name, root); dir = debugfs_create_dir(ent->name, root);
if (!dir) { if (!dir) {
...@@ -178,9 +205,8 @@ static int scom_debug_init_one(struct dentry *root, struct device_node *dn, ...@@ -178,9 +205,8 @@ static int scom_debug_init_one(struct dentry *root, struct device_node *dn,
return -1; return -1;
} }
debugfs_create_file("addr", 0600, dir, ent, &scom_addr_fops); debugfs_create_blob("devspec", 0400, dir, &ent->path);
debugfs_create_file("value", 0600, dir, ent, &scom_val_fops); debugfs_create_file("access", 0600, dir, ent, &scom_debug_fops);
debugfs_create_blob("devspec", 0400, dir, &ent->blob);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment