|
@@ -15,6 +15,7 @@
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/string.h>
|
|
|
#include <linux/vmalloc.h>
|
|
|
+#include <linux/mm.h>
|
|
|
#include <asm/ebcdic.h>
|
|
|
#include "hypfs.h"
|
|
|
|
|
@@ -22,6 +23,8 @@
|
|
|
#define CPU_NAME_LEN 16 /* type name len of cpus in diag224 name table */
|
|
|
#define TMP_SIZE 64 /* size of temporary buffers */
|
|
|
|
|
|
+#define DBFS_D204_HDR_VERSION 0
|
|
|
+
|
|
|
/* diag 204 subcodes */
|
|
|
enum diag204_sc {
|
|
|
SUBC_STIB4 = 4,
|
|
@@ -47,6 +50,8 @@ static void *diag204_buf; /* 4K aligned buffer for diag204 data */
|
|
|
static void *diag204_buf_vmalloc; /* vmalloc pointer for diag204 data */
|
|
|
static int diag204_buf_pages; /* number of pages for diag204 data */
|
|
|
|
|
|
+static struct dentry *dbfs_d204_file;
|
|
|
+
|
|
|
/*
|
|
|
* DIAG 204 data structures and member access functions.
|
|
|
*
|
|
@@ -364,18 +369,21 @@ static void diag204_free_buffer(void)
|
|
|
} else {
|
|
|
free_pages((unsigned long) diag204_buf, 0);
|
|
|
}
|
|
|
- diag204_buf_pages = 0;
|
|
|
diag204_buf = NULL;
|
|
|
}
|
|
|
|
|
|
+static void *page_align_ptr(void *ptr)
|
|
|
+{
|
|
|
+ return (void *) PAGE_ALIGN((unsigned long) ptr);
|
|
|
+}
|
|
|
+
|
|
|
static void *diag204_alloc_vbuf(int pages)
|
|
|
{
|
|
|
/* The buffer has to be page aligned! */
|
|
|
diag204_buf_vmalloc = vmalloc(PAGE_SIZE * (pages + 1));
|
|
|
if (!diag204_buf_vmalloc)
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
- diag204_buf = (void*)((unsigned long)diag204_buf_vmalloc
|
|
|
- & ~0xfffUL) + 0x1000;
|
|
|
+ diag204_buf = page_align_ptr(diag204_buf_vmalloc);
|
|
|
diag204_buf_pages = pages;
|
|
|
return diag204_buf;
|
|
|
}
|
|
@@ -468,17 +476,26 @@ fail_alloc:
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
+static int diag204_do_store(void *buf, int pages)
|
|
|
+{
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ rc = diag204((unsigned long) diag204_store_sc |
|
|
|
+ (unsigned long) diag204_info_type, pages, buf);
|
|
|
+ return rc < 0 ? -ENOSYS : 0;
|
|
|
+}
|
|
|
+
|
|
|
static void *diag204_store(void)
|
|
|
{
|
|
|
void *buf;
|
|
|
- int pages;
|
|
|
+ int pages, rc;
|
|
|
|
|
|
buf = diag204_get_buffer(diag204_info_type, &pages);
|
|
|
if (IS_ERR(buf))
|
|
|
goto out;
|
|
|
- if (diag204((unsigned long)diag204_store_sc |
|
|
|
- (unsigned long)diag204_info_type, pages, buf) < 0)
|
|
|
- return ERR_PTR(-ENOSYS);
|
|
|
+ rc = diag204_do_store(buf, pages);
|
|
|
+ if (rc)
|
|
|
+ return ERR_PTR(rc);
|
|
|
out:
|
|
|
return buf;
|
|
|
}
|
|
@@ -526,6 +543,92 @@ static int diag224_idx2name(int index, char *name)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+struct dbfs_d204_hdr {
|
|
|
+ u64 len; /* Length of d204 buffer without header */
|
|
|
+ u16 version; /* Version of header */
|
|
|
+ u8 sc; /* Used subcode */
|
|
|
+ char reserved[53];
|
|
|
+} __attribute__ ((packed));
|
|
|
+
|
|
|
+struct dbfs_d204 {
|
|
|
+ struct dbfs_d204_hdr hdr; /* 64 byte header */
|
|
|
+ char buf[]; /* d204 buffer */
|
|
|
+} __attribute__ ((packed));
|
|
|
+
|
|
|
+struct dbfs_d204_private {
|
|
|
+ struct dbfs_d204 *d204; /* Aligned d204 data with header */
|
|
|
+ void *base; /* Base pointer (needed for vfree) */
|
|
|
+};
|
|
|
+
|
|
|
+static int dbfs_d204_open(struct inode *inode, struct file *file)
|
|
|
+{
|
|
|
+ struct dbfs_d204_private *data;
|
|
|
+ struct dbfs_d204 *d204;
|
|
|
+ int rc, buf_size;
|
|
|
+
|
|
|
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
|
|
|
+ if (!data)
|
|
|
+ return -ENOMEM;
|
|
|
+ buf_size = PAGE_SIZE * (diag204_buf_pages + 1) + sizeof(d204->hdr);
|
|
|
+ data->base = vmalloc(buf_size);
|
|
|
+ if (!data->base) {
|
|
|
+ rc = -ENOMEM;
|
|
|
+ goto fail_kfree_data;
|
|
|
+ }
|
|
|
+ memset(data->base, 0, buf_size);
|
|
|
+ d204 = page_align_ptr(data->base + sizeof(d204->hdr))
|
|
|
+ - sizeof(d204->hdr);
|
|
|
+ rc = diag204_do_store(&d204->buf, diag204_buf_pages);
|
|
|
+ if (rc)
|
|
|
+ goto fail_vfree_base;
|
|
|
+ d204->hdr.version = DBFS_D204_HDR_VERSION;
|
|
|
+ d204->hdr.len = PAGE_SIZE * diag204_buf_pages;
|
|
|
+ d204->hdr.sc = diag204_store_sc;
|
|
|
+ data->d204 = d204;
|
|
|
+ file->private_data = data;
|
|
|
+ return nonseekable_open(inode, file);
|
|
|
+
|
|
|
+fail_vfree_base:
|
|
|
+ vfree(data->base);
|
|
|
+fail_kfree_data:
|
|
|
+ kfree(data);
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+static int dbfs_d204_release(struct inode *inode, struct file *file)
|
|
|
+{
|
|
|
+ struct dbfs_d204_private *data = file->private_data;
|
|
|
+
|
|
|
+ vfree(data->base);
|
|
|
+ kfree(data);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static ssize_t dbfs_d204_read(struct file *file, char __user *buf,
|
|
|
+ size_t size, loff_t *ppos)
|
|
|
+{
|
|
|
+ struct dbfs_d204_private *data = file->private_data;
|
|
|
+
|
|
|
+ return simple_read_from_buffer(buf, size, ppos, data->d204,
|
|
|
+ data->d204->hdr.len +
|
|
|
+ sizeof(data->d204->hdr));
|
|
|
+}
|
|
|
+
|
|
|
+static const struct file_operations dbfs_d204_ops = {
|
|
|
+ .open = dbfs_d204_open,
|
|
|
+ .read = dbfs_d204_read,
|
|
|
+ .release = dbfs_d204_release,
|
|
|
+};
|
|
|
+
|
|
|
+static int hypfs_dbfs_init(void)
|
|
|
+{
|
|
|
+ dbfs_d204_file = debugfs_create_file("diag_204", 0400, hypfs_dbfs_dir,
|
|
|
+ NULL, &dbfs_d204_ops);
|
|
|
+ if (IS_ERR(dbfs_d204_file))
|
|
|
+ return PTR_ERR(dbfs_d204_file);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
__init int hypfs_diag_init(void)
|
|
|
{
|
|
|
int rc;
|
|
@@ -540,11 +643,17 @@ __init int hypfs_diag_init(void)
|
|
|
pr_err("The hardware system does not provide all "
|
|
|
"functions required by hypfs\n");
|
|
|
}
|
|
|
+ if (diag204_info_type == INFO_EXT) {
|
|
|
+ rc = hypfs_dbfs_init();
|
|
|
+ if (rc)
|
|
|
+ diag204_free_buffer();
|
|
|
+ }
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
void hypfs_diag_exit(void)
|
|
|
{
|
|
|
+ debugfs_remove(dbfs_d204_file);
|
|
|
diag224_delete_name_table();
|
|
|
diag204_free_buffer();
|
|
|
}
|