0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015 #include <linux/fs.h>
0016 #include <linux/init.h>
0017 #include <linux/compat.h>
0018 #include <linux/kernel.h>
0019 #include <linux/miscdevice.h>
0020 #include <linux/slab.h>
0021 #include <linux/uaccess.h>
0022 #include <linux/export.h>
0023 #include <linux/mutex.h>
0024 #include <linux/cma.h>
0025 #include <linux/mm.h>
0026 #include <asm/cpcmd.h>
0027 #include <asm/debug.h>
0028 #include <asm/vmcp.h>
0029
0030 struct vmcp_session {
0031 char *response;
0032 unsigned int bufsize;
0033 unsigned int cma_alloc : 1;
0034 int resp_size;
0035 int resp_code;
0036 struct mutex mutex;
0037 };
0038
0039 static debug_info_t *vmcp_debug;
0040
0041 static unsigned long vmcp_cma_size __initdata = CONFIG_VMCP_CMA_SIZE * 1024 * 1024;
0042 static struct cma *vmcp_cma;
0043
0044 static int __init early_parse_vmcp_cma(char *p)
0045 {
0046 if (!p)
0047 return 1;
0048 vmcp_cma_size = ALIGN(memparse(p, NULL), PAGE_SIZE);
0049 return 0;
0050 }
0051 early_param("vmcp_cma", early_parse_vmcp_cma);
0052
0053 void __init vmcp_cma_reserve(void)
0054 {
0055 if (!MACHINE_IS_VM)
0056 return;
0057 cma_declare_contiguous(0, vmcp_cma_size, 0, 0, 0, false, "vmcp", &vmcp_cma);
0058 }
0059
0060 static void vmcp_response_alloc(struct vmcp_session *session)
0061 {
0062 struct page *page = NULL;
0063 int nr_pages, order;
0064
0065 order = get_order(session->bufsize);
0066 nr_pages = ALIGN(session->bufsize, PAGE_SIZE) >> PAGE_SHIFT;
0067
0068
0069
0070
0071
0072 if (order > 2)
0073 page = cma_alloc(vmcp_cma, nr_pages, 0, false);
0074 if (page) {
0075 session->response = (char *)page_to_virt(page);
0076 session->cma_alloc = 1;
0077 return;
0078 }
0079 session->response = (char *)__get_free_pages(GFP_KERNEL | __GFP_RETRY_MAYFAIL, order);
0080 }
0081
0082 static void vmcp_response_free(struct vmcp_session *session)
0083 {
0084 int nr_pages, order;
0085 struct page *page;
0086
0087 if (!session->response)
0088 return;
0089 order = get_order(session->bufsize);
0090 nr_pages = ALIGN(session->bufsize, PAGE_SIZE) >> PAGE_SHIFT;
0091 if (session->cma_alloc) {
0092 page = virt_to_page((unsigned long)session->response);
0093 cma_release(vmcp_cma, page, nr_pages);
0094 session->cma_alloc = 0;
0095 } else {
0096 free_pages((unsigned long)session->response, order);
0097 }
0098 session->response = NULL;
0099 }
0100
0101 static int vmcp_open(struct inode *inode, struct file *file)
0102 {
0103 struct vmcp_session *session;
0104
0105 if (!capable(CAP_SYS_ADMIN))
0106 return -EPERM;
0107
0108 session = kmalloc(sizeof(*session), GFP_KERNEL);
0109 if (!session)
0110 return -ENOMEM;
0111
0112 session->bufsize = PAGE_SIZE;
0113 session->response = NULL;
0114 session->resp_size = 0;
0115 mutex_init(&session->mutex);
0116 file->private_data = session;
0117 return nonseekable_open(inode, file);
0118 }
0119
0120 static int vmcp_release(struct inode *inode, struct file *file)
0121 {
0122 struct vmcp_session *session;
0123
0124 session = file->private_data;
0125 file->private_data = NULL;
0126 vmcp_response_free(session);
0127 kfree(session);
0128 return 0;
0129 }
0130
0131 static ssize_t
0132 vmcp_read(struct file *file, char __user *buff, size_t count, loff_t *ppos)
0133 {
0134 ssize_t ret;
0135 size_t size;
0136 struct vmcp_session *session;
0137
0138 session = file->private_data;
0139 if (mutex_lock_interruptible(&session->mutex))
0140 return -ERESTARTSYS;
0141 if (!session->response) {
0142 mutex_unlock(&session->mutex);
0143 return 0;
0144 }
0145 size = min_t(size_t, session->resp_size, session->bufsize);
0146 ret = simple_read_from_buffer(buff, count, ppos,
0147 session->response, size);
0148
0149 mutex_unlock(&session->mutex);
0150
0151 return ret;
0152 }
0153
0154 static ssize_t
0155 vmcp_write(struct file *file, const char __user *buff, size_t count,
0156 loff_t *ppos)
0157 {
0158 char *cmd;
0159 struct vmcp_session *session;
0160
0161 if (count > 240)
0162 return -EINVAL;
0163 cmd = memdup_user_nul(buff, count);
0164 if (IS_ERR(cmd))
0165 return PTR_ERR(cmd);
0166 session = file->private_data;
0167 if (mutex_lock_interruptible(&session->mutex)) {
0168 kfree(cmd);
0169 return -ERESTARTSYS;
0170 }
0171 if (!session->response)
0172 vmcp_response_alloc(session);
0173 if (!session->response) {
0174 mutex_unlock(&session->mutex);
0175 kfree(cmd);
0176 return -ENOMEM;
0177 }
0178 debug_text_event(vmcp_debug, 1, cmd);
0179 session->resp_size = cpcmd(cmd, session->response, session->bufsize,
0180 &session->resp_code);
0181 mutex_unlock(&session->mutex);
0182 kfree(cmd);
0183 *ppos = 0;
0184 return count;
0185 }
0186
0187
0188
0189
0190
0191
0192
0193
0194
0195
0196
0197
0198
0199
0200 static long vmcp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
0201 {
0202 struct vmcp_session *session;
0203 int ret = -ENOTTY;
0204 int __user *argp;
0205
0206 session = file->private_data;
0207 if (is_compat_task())
0208 argp = compat_ptr(arg);
0209 else
0210 argp = (int __user *)arg;
0211 if (mutex_lock_interruptible(&session->mutex))
0212 return -ERESTARTSYS;
0213 switch (cmd) {
0214 case VMCP_GETCODE:
0215 ret = put_user(session->resp_code, argp);
0216 break;
0217 case VMCP_SETBUF:
0218 vmcp_response_free(session);
0219 ret = get_user(session->bufsize, argp);
0220 if (ret)
0221 session->bufsize = PAGE_SIZE;
0222 if (!session->bufsize || get_order(session->bufsize) > 8) {
0223 session->bufsize = PAGE_SIZE;
0224 ret = -EINVAL;
0225 }
0226 break;
0227 case VMCP_GETSIZE:
0228 ret = put_user(session->resp_size, argp);
0229 break;
0230 default:
0231 break;
0232 }
0233 mutex_unlock(&session->mutex);
0234 return ret;
0235 }
0236
0237 static const struct file_operations vmcp_fops = {
0238 .owner = THIS_MODULE,
0239 .open = vmcp_open,
0240 .release = vmcp_release,
0241 .read = vmcp_read,
0242 .write = vmcp_write,
0243 .unlocked_ioctl = vmcp_ioctl,
0244 .compat_ioctl = vmcp_ioctl,
0245 .llseek = no_llseek,
0246 };
0247
0248 static struct miscdevice vmcp_dev = {
0249 .name = "vmcp",
0250 .minor = MISC_DYNAMIC_MINOR,
0251 .fops = &vmcp_fops,
0252 };
0253
0254 static int __init vmcp_init(void)
0255 {
0256 int ret;
0257
0258 if (!MACHINE_IS_VM)
0259 return 0;
0260
0261 vmcp_debug = debug_register("vmcp", 1, 1, 240);
0262 if (!vmcp_debug)
0263 return -ENOMEM;
0264
0265 ret = debug_register_view(vmcp_debug, &debug_hex_ascii_view);
0266 if (ret) {
0267 debug_unregister(vmcp_debug);
0268 return ret;
0269 }
0270
0271 ret = misc_register(&vmcp_dev);
0272 if (ret)
0273 debug_unregister(vmcp_debug);
0274 return ret;
0275 }
0276 device_initcall(vmcp_init);