0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014 #include <linux/kernel.h>
0015 #include <linux/module.h>
0016 #include <linux/uaccess.h>
0017 #include <acpi/apei.h>
0018 #include <linux/miscdevice.h>
0019
0020 #include "apei-internal.h"
0021
0022 #define ERST_DBG_PFX "ERST DBG: "
0023
0024 #define ERST_DBG_RECORD_LEN_MAX 0x4000
0025
0026 static void *erst_dbg_buf;
0027 static unsigned int erst_dbg_buf_len;
0028
0029
0030 static DEFINE_MUTEX(erst_dbg_mutex);
0031
0032 static int erst_dbg_open(struct inode *inode, struct file *file)
0033 {
0034 int rc, *pos;
0035
0036 if (erst_disable)
0037 return -ENODEV;
0038
0039 pos = (int *)&file->private_data;
0040
0041 rc = erst_get_record_id_begin(pos);
0042 if (rc)
0043 return rc;
0044
0045 return nonseekable_open(inode, file);
0046 }
0047
0048 static int erst_dbg_release(struct inode *inode, struct file *file)
0049 {
0050 erst_get_record_id_end();
0051
0052 return 0;
0053 }
0054
0055 static long erst_dbg_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
0056 {
0057 int rc;
0058 u64 record_id;
0059 u32 record_count;
0060
0061 switch (cmd) {
0062 case APEI_ERST_CLEAR_RECORD:
0063 rc = copy_from_user(&record_id, (void __user *)arg,
0064 sizeof(record_id));
0065 if (rc)
0066 return -EFAULT;
0067 return erst_clear(record_id);
0068 case APEI_ERST_GET_RECORD_COUNT:
0069 rc = erst_get_record_count();
0070 if (rc < 0)
0071 return rc;
0072 record_count = rc;
0073 rc = put_user(record_count, (u32 __user *)arg);
0074 if (rc)
0075 return rc;
0076 return 0;
0077 default:
0078 return -ENOTTY;
0079 }
0080 }
0081
0082 static ssize_t erst_dbg_read(struct file *filp, char __user *ubuf,
0083 size_t usize, loff_t *off)
0084 {
0085 int rc, *pos;
0086 ssize_t len = 0;
0087 u64 id;
0088
0089 if (*off)
0090 return -EINVAL;
0091
0092 if (mutex_lock_interruptible(&erst_dbg_mutex) != 0)
0093 return -EINTR;
0094
0095 pos = (int *)&filp->private_data;
0096
0097 retry_next:
0098 rc = erst_get_record_id_next(pos, &id);
0099 if (rc)
0100 goto out;
0101
0102 if (id == APEI_ERST_INVALID_RECORD_ID) {
0103
0104
0105
0106
0107
0108
0109 len = 0;
0110
0111 goto out;
0112 }
0113 retry:
0114 rc = len = erst_read_record(id, erst_dbg_buf, erst_dbg_buf_len,
0115 erst_dbg_buf_len, NULL);
0116
0117 if (rc == -ENOENT)
0118 goto retry_next;
0119 if (rc < 0)
0120 goto out;
0121 if (len > ERST_DBG_RECORD_LEN_MAX) {
0122 pr_warn(ERST_DBG_PFX
0123 "Record (ID: 0x%llx) length is too long: %zd\n", id, len);
0124 rc = -EIO;
0125 goto out;
0126 }
0127 if (len > erst_dbg_buf_len) {
0128 void *p;
0129 rc = -ENOMEM;
0130 p = kmalloc(len, GFP_KERNEL);
0131 if (!p)
0132 goto out;
0133 kfree(erst_dbg_buf);
0134 erst_dbg_buf = p;
0135 erst_dbg_buf_len = len;
0136 goto retry;
0137 }
0138
0139 rc = -EINVAL;
0140 if (len > usize)
0141 goto out;
0142
0143 rc = -EFAULT;
0144 if (copy_to_user(ubuf, erst_dbg_buf, len))
0145 goto out;
0146 rc = 0;
0147 out:
0148 mutex_unlock(&erst_dbg_mutex);
0149 return rc ? rc : len;
0150 }
0151
0152 static ssize_t erst_dbg_write(struct file *filp, const char __user *ubuf,
0153 size_t usize, loff_t *off)
0154 {
0155 int rc;
0156 struct cper_record_header *rcd;
0157
0158 if (!capable(CAP_SYS_ADMIN))
0159 return -EPERM;
0160
0161 if (usize > ERST_DBG_RECORD_LEN_MAX) {
0162 pr_err(ERST_DBG_PFX "Too long record to be written\n");
0163 return -EINVAL;
0164 }
0165
0166 if (mutex_lock_interruptible(&erst_dbg_mutex))
0167 return -EINTR;
0168 if (usize > erst_dbg_buf_len) {
0169 void *p;
0170 rc = -ENOMEM;
0171 p = kmalloc(usize, GFP_KERNEL);
0172 if (!p)
0173 goto out;
0174 kfree(erst_dbg_buf);
0175 erst_dbg_buf = p;
0176 erst_dbg_buf_len = usize;
0177 }
0178 rc = copy_from_user(erst_dbg_buf, ubuf, usize);
0179 if (rc) {
0180 rc = -EFAULT;
0181 goto out;
0182 }
0183 rcd = erst_dbg_buf;
0184 rc = -EINVAL;
0185 if (rcd->record_length != usize)
0186 goto out;
0187
0188 rc = erst_write(erst_dbg_buf);
0189
0190 out:
0191 mutex_unlock(&erst_dbg_mutex);
0192 return rc < 0 ? rc : usize;
0193 }
0194
0195 static const struct file_operations erst_dbg_ops = {
0196 .owner = THIS_MODULE,
0197 .open = erst_dbg_open,
0198 .release = erst_dbg_release,
0199 .read = erst_dbg_read,
0200 .write = erst_dbg_write,
0201 .unlocked_ioctl = erst_dbg_ioctl,
0202 .llseek = no_llseek,
0203 };
0204
0205 static struct miscdevice erst_dbg_dev = {
0206 .minor = MISC_DYNAMIC_MINOR,
0207 .name = "erst_dbg",
0208 .fops = &erst_dbg_ops,
0209 };
0210
0211 static __init int erst_dbg_init(void)
0212 {
0213 if (erst_disable) {
0214 pr_info(ERST_DBG_PFX "ERST support is disabled.\n");
0215 return -ENODEV;
0216 }
0217 return misc_register(&erst_dbg_dev);
0218 }
0219
0220 static __exit void erst_dbg_exit(void)
0221 {
0222 misc_deregister(&erst_dbg_dev);
0223 kfree(erst_dbg_buf);
0224 }
0225
0226 module_init(erst_dbg_init);
0227 module_exit(erst_dbg_exit);
0228
0229 MODULE_AUTHOR("Huang Ying");
0230 MODULE_DESCRIPTION("APEI Error Record Serialization Table debug support");
0231 MODULE_LICENSE("GPL");