blob: 8a6b57108ac26120cc25a037499d15432e6a0851 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
Jeff Dike827b3f62008-02-04 22:31:29 -08003 * Copyright (C) 2001 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * Licensed under the GPL
5 */
6
Jeff Dike827b3f62008-02-04 22:31:29 -08007#include <linux/console.h>
8#include <linux/ctype.h>
André Goddard Rosae7d28602009-12-14 18:01:06 -08009#include <linux/string.h>
Jeff Dike827b3f62008-02-04 22:31:29 -080010#include <linux/interrupt.h>
11#include <linux/list.h>
12#include <linux/mm.h>
13#include <linux/module.h>
14#include <linux/notifier.h>
15#include <linux/reboot.h>
16#include <linux/proc_fs.h>
17#include <linux/slab.h>
18#include <linux/syscalls.h>
19#include <linux/utsname.h>
Balbir Singh36137122008-12-09 13:14:07 -080020#include <linux/socket.h>
21#include <linux/un.h>
Jeff Dike827b3f62008-02-04 22:31:29 -080022#include <linux/workqueue.h>
23#include <linux/mutex.h>
Al Viro723a1d72012-08-19 13:00:49 -040024#include <linux/fs.h>
25#include <linux/mount.h>
26#include <linux/file.h>
Jeff Dike827b3f62008-02-04 22:31:29 -080027#include <asm/uaccess.h>
Richard Weinbergera3a85a72012-03-29 18:47:46 +020028#include <asm/switch_to.h>
Jeff Dike827b3f62008-02-04 22:31:29 -080029
Al Viro37185b32012-10-08 03:27:32 +010030#include <init.h>
31#include <irq_kern.h>
32#include <irq_user.h>
33#include <kern_util.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034#include "mconsole.h"
35#include "mconsole_kern.h"
Al Viro37185b32012-10-08 03:27:32 +010036#include <os.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
Jeff Diked50084a2006-01-06 00:18:50 -080038static int do_unlink_socket(struct notifier_block *notifier,
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 unsigned long what, void *data)
40{
Jeff Dikeae2587e2007-10-16 01:26:57 -070041 return mconsole_unlink_socket();
Linus Torvalds1da177e2005-04-16 15:20:36 -070042}
43
44
45static struct notifier_block reboot_notifier = {
46 .notifier_call = do_unlink_socket,
47 .priority = 0,
48};
49
Jeff Diked50084a2006-01-06 00:18:50 -080050/* Safe without explicit locking for now. Tasklets provide their own
Linus Torvalds1da177e2005-04-16 15:20:36 -070051 * locking, and the interrupt handler is safe because it can't interrupt
52 * itself and it can only happen on CPU 0.
53 */
54
Jeff Dike90107722006-01-06 00:18:54 -080055static LIST_HEAD(mc_requests);
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
David Howells6d5aefb2006-12-05 19:36:26 +000057static void mc_work_proc(struct work_struct *unused)
Linus Torvalds1da177e2005-04-16 15:20:36 -070058{
59 struct mconsole_entry *req;
60 unsigned long flags;
61
Jeff Dikeae2587e2007-10-16 01:26:57 -070062 while (!list_empty(&mc_requests)) {
Paolo 'Blaisorblade' Giarrussodbdb4c02006-04-10 22:53:39 -070063 local_irq_save(flags);
Jeff Dikeae2587e2007-10-16 01:26:57 -070064 req = list_entry(mc_requests.next, struct mconsole_entry, list);
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 list_del(&req->list);
66 local_irq_restore(flags);
67 req->request.cmd->handler(&req->request);
68 kfree(req);
69 }
70}
71
David Howells6d5aefb2006-12-05 19:36:26 +000072static DECLARE_WORK(mconsole_work, mc_work_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
Al Viro7bea96f2006-10-08 22:49:34 +010074static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070075{
76 /* long to avoid size mismatch warnings from gcc */
77 long fd;
78 struct mconsole_entry *new;
Al Viro3a512372006-10-24 11:15:29 +010079 static struct mc_request req; /* that's OK */
Linus Torvalds1da177e2005-04-16 15:20:36 -070080
81 fd = (long) dev_id;
Jeff Dikeae2587e2007-10-16 01:26:57 -070082 while (mconsole_get_request(fd, &req)) {
83 if (req.cmd->context == MCONSOLE_INTR)
Linus Torvalds1da177e2005-04-16 15:20:36 -070084 (*req.cmd->handler)(&req);
85 else {
Jeff Dike60baa152006-04-10 22:53:28 -070086 new = kmalloc(sizeof(*new), GFP_NOWAIT);
Jeff Dikeae2587e2007-10-16 01:26:57 -070087 if (new == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 mconsole_reply(&req, "Out of memory", 1, 0);
89 else {
90 new->request = req;
Al Viro3a512372006-10-24 11:15:29 +010091 new->request.regs = get_irq_regs()->regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 list_add(&new->list, &mc_requests);
93 }
94 }
95 }
Jeff Dikeae2587e2007-10-16 01:26:57 -070096 if (!list_empty(&mc_requests))
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 schedule_work(&mconsole_work);
98 reactivate_fd(fd, MCONSOLE_IRQ);
Jeff Dikeae2587e2007-10-16 01:26:57 -070099 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100}
101
102void mconsole_version(struct mc_request *req)
103{
104 char version[256];
105
Serge E. Hallyne9ff3992006-10-02 02:18:11 -0700106 sprintf(version, "%s %s %s %s %s", utsname()->sysname,
Jeff Dikeae2587e2007-10-16 01:26:57 -0700107 utsname()->nodename, utsname()->release, utsname()->version,
108 utsname()->machine);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 mconsole_reply(req, version, 0, 0);
110}
111
112void mconsole_log(struct mc_request *req)
113{
114 int len;
115 char *ptr = req->request.data;
116
117 ptr += strlen("log ");
118
119 len = req->len - (ptr - req->request.data);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700120 printk(KERN_WARNING "%.*s", len, ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 mconsole_reply(req, "", 0, 0);
122}
123
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124void mconsole_proc(struct mc_request *req)
125{
Eric W. Biederman17cf22c2010-03-02 14:51:53 -0800126 struct vfsmount *mnt = task_active_pid_ns(current)->proc_mnt;
Al Viro723a1d72012-08-19 13:00:49 -0400127 char *buf;
128 int len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 struct file *file;
Al Viro723a1d72012-08-19 13:00:49 -0400130 int first_chunk = 1;
131 char *ptr = req->request.data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132
133 ptr += strlen("proc");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800134 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135
Jann Horn378c6522016-03-22 14:25:36 -0700136 file = file_open_root(mnt->mnt_root, mnt, ptr, O_RDONLY, 0);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700137 if (IS_ERR(file)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 mconsole_reply(req, "Failed to open file", 1, 0);
Al Viro723a1d72012-08-19 13:00:49 -0400139 printk(KERN_ERR "open /proc/%s: %ld\n", ptr, PTR_ERR(file));
Al Viro4ecf09f2009-12-24 00:44:44 -0500140 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142
143 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700144 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
146 goto out_fput;
147 }
148
Al Viro723a1d72012-08-19 13:00:49 -0400149 do {
Al Viroc9036e92013-06-19 12:35:42 +0400150 loff_t pos = file->f_pos;
Al Viro723a1d72012-08-19 13:00:49 -0400151 mm_segment_t old_fs = get_fs();
152 set_fs(KERNEL_DS);
153 len = vfs_read(file, buf, PAGE_SIZE - 1, &pos);
154 set_fs(old_fs);
155 file->f_pos = pos;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 if (len < 0) {
157 mconsole_reply(req, "Read of file failed", 1, 0);
158 goto out_free;
159 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700160 /* Begin the file content on his own line. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 if (first_chunk) {
162 mconsole_reply(req, "\n", 0, 1);
163 first_chunk = 0;
164 }
Al Viro723a1d72012-08-19 13:00:49 -0400165 buf[len] = '\0';
166 mconsole_reply(req, buf, 0, (len > 0));
167 } while (len > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 out_free:
169 kfree(buf);
Al Viro723a1d72012-08-19 13:00:49 -0400170 out_fput:
171 fput(file);
172 out: ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173}
174
175#define UML_MCONSOLE_HELPTEXT \
176"Commands: \n\
177 version - Get kernel version \n\
178 help - Print this message \n\
179 halt - Halt UML \n\
180 reboot - Reboot UML \n\
181 config <dev>=<config> - Add a new device to UML; \n\
182 same syntax as command line \n\
183 config <dev> - Query the configuration of a device \n\
184 remove <dev> - Remove a device from UML \n\
185 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
Jeff Dikedb805812006-02-01 03:06:23 -0800186 cad - invoke the Ctrl-Alt-Del handler \n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 stop - pause the UML; it will do nothing until it receives a 'go' \n\
188 go - continue the UML after a 'stop' \n\
189 log <string> - make UML enter <string> into the kernel log\n\
190 proc <file> - returns the contents of the UML's /proc/<file>\n\
Jeff Dike3eddddc2005-09-16 19:27:46 -0700191 stack <pid> - returns the stack of the specified pid\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192"
193
194void mconsole_help(struct mc_request *req)
195{
196 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
197}
198
199void mconsole_halt(struct mc_request *req)
200{
201 mconsole_reply(req, "", 0, 0);
202 machine_halt();
203}
204
205void mconsole_reboot(struct mc_request *req)
206{
207 mconsole_reply(req, "", 0, 0);
208 machine_restart(NULL);
209}
210
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211void mconsole_cad(struct mc_request *req)
212{
213 mconsole_reply(req, "", 0, 0);
214 ctrl_alt_del();
215}
216
217void mconsole_go(struct mc_request *req)
218{
219 mconsole_reply(req, "Not stopped", 1, 0);
220}
221
222void mconsole_stop(struct mc_request *req)
223{
224 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
225 os_set_fd_block(req->originating_fd, 1);
Al Viro3a512372006-10-24 11:15:29 +0100226 mconsole_reply(req, "stopped", 0, 0);
Karol Swietlickicc0be0f2008-02-04 22:31:25 -0800227 for (;;) {
228 if (!mconsole_get_request(req->originating_fd, req))
229 continue;
Al Viro3a512372006-10-24 11:15:29 +0100230 if (req->cmd->handler == mconsole_go)
231 break;
232 if (req->cmd->handler == mconsole_stop) {
233 mconsole_reply(req, "Already stopped", 1, 0);
234 continue;
235 }
236 if (req->cmd->handler == mconsole_sysrq) {
237 struct pt_regs *old_regs;
238 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
239 mconsole_sysrq(req);
240 set_irq_regs(old_regs);
241 continue;
242 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 (*req->cmd->handler)(req);
244 }
245 os_set_fd_block(req->originating_fd, 0);
246 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
247 mconsole_reply(req, "", 0, 0);
248}
249
Jeff Dike84f48d42007-02-10 01:44:01 -0800250static DEFINE_SPINLOCK(mc_devices_lock);
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800251static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252
253void mconsole_register_dev(struct mc_device *new)
254{
Jeff Dike84f48d42007-02-10 01:44:01 -0800255 spin_lock(&mc_devices_lock);
256 BUG_ON(!list_empty(&new->list));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 list_add(&new->list, &mconsole_devices);
Jeff Dike84f48d42007-02-10 01:44:01 -0800258 spin_unlock(&mc_devices_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259}
260
261static struct mc_device *mconsole_find_dev(char *name)
262{
263 struct list_head *ele;
264 struct mc_device *dev;
265
Jeff Dikeae2587e2007-10-16 01:26:57 -0700266 list_for_each(ele, &mconsole_devices) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 dev = list_entry(ele, struct mc_device, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700268 if (!strncmp(name, dev->name, strlen(dev->name)))
269 return dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700271 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272}
273
Jeff Dike02dea082006-03-31 02:30:08 -0800274#define UNPLUGGED_PER_PAGE \
275 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
276
277struct unplugged_pages {
278 struct list_head list;
279 void *pages[UNPLUGGED_PER_PAGE];
280};
281
Daniel Walkere98fa282008-02-04 22:31:27 -0800282static DEFINE_MUTEX(plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800283static unsigned long long unplugged_pages_count = 0;
Jeff Dikec59bce62007-02-10 01:44:04 -0800284static LIST_HEAD(unplugged_pages);
Jeff Dike02dea082006-03-31 02:30:08 -0800285static int unplug_index = UNPLUGGED_PER_PAGE;
286
Jeff Dikef28169d2007-02-10 01:43:53 -0800287static int mem_config(char *str, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800288{
289 unsigned long long diff;
290 int err = -EINVAL, i, add;
291 char *ret;
292
Jeff Dikeae2587e2007-10-16 01:26:57 -0700293 if (str[0] != '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800294 *error_out = "Expected '=' after 'mem'";
Jeff Dike02dea082006-03-31 02:30:08 -0800295 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800296 }
Jeff Dike02dea082006-03-31 02:30:08 -0800297
298 str++;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700299 if (str[0] == '-')
Jeff Dike02dea082006-03-31 02:30:08 -0800300 add = 0;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700301 else if (str[0] == '+') {
Jeff Dike02dea082006-03-31 02:30:08 -0800302 add = 1;
303 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800304 else {
305 *error_out = "Expected increment to start with '-' or '+'";
306 goto out;
307 }
Jeff Dike02dea082006-03-31 02:30:08 -0800308
309 str++;
310 diff = memparse(str, &ret);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700311 if (*ret != '\0') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800312 *error_out = "Failed to parse memory increment";
Jeff Dike02dea082006-03-31 02:30:08 -0800313 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800314 }
Jeff Dike02dea082006-03-31 02:30:08 -0800315
316 diff /= PAGE_SIZE;
317
Daniel Walkere98fa282008-02-04 22:31:27 -0800318 mutex_lock(&plug_mem_mutex);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700319 for (i = 0; i < diff; i++) {
Jeff Dike02dea082006-03-31 02:30:08 -0800320 struct unplugged_pages *unplugged;
321 void *addr;
322
Jeff Dikeae2587e2007-10-16 01:26:57 -0700323 if (add) {
324 if (list_empty(&unplugged_pages))
Jeff Dike02dea082006-03-31 02:30:08 -0800325 break;
326
327 unplugged = list_entry(unplugged_pages.next,
328 struct unplugged_pages, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700329 if (unplug_index > 0)
Jeff Dike02dea082006-03-31 02:30:08 -0800330 addr = unplugged->pages[--unplug_index];
331 else {
332 list_del(&unplugged->list);
333 addr = unplugged;
334 unplug_index = UNPLUGGED_PER_PAGE;
335 }
336
337 free_page((unsigned long) addr);
338 unplugged_pages_count--;
339 }
340 else {
341 struct page *page;
342
343 page = alloc_page(GFP_ATOMIC);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700344 if (page == NULL)
Jeff Dike02dea082006-03-31 02:30:08 -0800345 break;
346
347 unplugged = page_address(page);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700348 if (unplug_index == UNPLUGGED_PER_PAGE) {
Jeff Dike02dea082006-03-31 02:30:08 -0800349 list_add(&unplugged->list, &unplugged_pages);
350 unplug_index = 0;
351 }
352 else {
353 struct list_head *entry = unplugged_pages.next;
354 addr = unplugged;
355
356 unplugged = list_entry(entry,
357 struct unplugged_pages,
358 list);
Jeff Dike02dea082006-03-31 02:30:08 -0800359 err = os_drop_memory(addr, PAGE_SIZE);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700360 if (err) {
361 printk(KERN_ERR "Failed to release "
362 "memory - errno = %d\n", err);
Jeff Dikef28169d2007-02-10 01:43:53 -0800363 *error_out = "Failed to release memory";
Jeff Dike84f48d42007-02-10 01:44:01 -0800364 goto out_unlock;
Jeff Dikef28169d2007-02-10 01:43:53 -0800365 }
366 unplugged->pages[unplug_index++] = addr;
Jeff Dike02dea082006-03-31 02:30:08 -0800367 }
368
369 unplugged_pages_count++;
370 }
371 }
372
373 err = 0;
Jeff Dike84f48d42007-02-10 01:44:01 -0800374out_unlock:
Daniel Walkere98fa282008-02-04 22:31:27 -0800375 mutex_unlock(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800376out:
377 return err;
378}
379
380static int mem_get_config(char *name, char *str, int size, char **error_out)
381{
382 char buf[sizeof("18446744073709551615")];
383 int len = 0;
384
385 sprintf(buf, "%ld", uml_physmem);
386 CONFIG_CHUNK(str, size, len, buf, 1);
387
388 return len;
389}
390
391static int mem_id(char **str, int *start_out, int *end_out)
392{
393 *start_out = 0;
394 *end_out = 0;
395
396 return 0;
397}
398
Jeff Dikef28169d2007-02-10 01:43:53 -0800399static int mem_remove(int n, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800400{
Jeff Dikef28169d2007-02-10 01:43:53 -0800401 *error_out = "Memory doesn't support the remove operation";
Jeff Dike02dea082006-03-31 02:30:08 -0800402 return -EBUSY;
403}
404
405static struct mc_device mem_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800406 .list = LIST_HEAD_INIT(mem_mc.list),
Jeff Dike02dea082006-03-31 02:30:08 -0800407 .name = "mem",
408 .config = mem_config,
409 .get_config = mem_get_config,
410 .id = mem_id,
411 .remove = mem_remove,
412};
413
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700414static int __init mem_mc_init(void)
Jeff Dike02dea082006-03-31 02:30:08 -0800415{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700416 if (can_drop_memory())
Jeff Dike02dea082006-03-31 02:30:08 -0800417 mconsole_register_dev(&mem_mc);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700418 else printk(KERN_ERR "Can't release memory to the host - memory "
419 "hotplug won't be supported\n");
Jeff Dike02dea082006-03-31 02:30:08 -0800420 return 0;
421}
422
423__initcall(mem_mc_init);
424
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425#define CONFIG_BUF_SIZE 64
426
Jeff Diked50084a2006-01-06 00:18:50 -0800427static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 char **),
429 struct mc_request *req, char *name)
430{
431 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
432 int n, size;
433
Jeff Dikeae2587e2007-10-16 01:26:57 -0700434 if (get_config == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 mconsole_reply(req, "No get_config routine defined", 1, 0);
436 return;
437 }
438
439 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700440 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 buf = default_buf;
442
Jeff Dikeae2587e2007-10-16 01:26:57 -0700443 while (1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 n = (*get_config)(name, buf, size, &error);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700445 if (error != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 mconsole_reply(req, error, 1, 0);
447 goto out;
448 }
449
Jeff Dikeae2587e2007-10-16 01:26:57 -0700450 if (n <= size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 mconsole_reply(req, buf, 0, 0);
452 goto out;
453 }
454
Jeff Dikeae2587e2007-10-16 01:26:57 -0700455 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 kfree(buf);
457
458 size = n;
459 buf = kmalloc(size, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700460 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
462 return;
463 }
464 }
465 out:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700466 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468}
469
470void mconsole_config(struct mc_request *req)
471{
472 struct mc_device *dev;
Jeff Dikef28169d2007-02-10 01:43:53 -0800473 char *ptr = req->request.data, *name, *error_string = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 int err;
475
476 ptr += strlen("config");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800477 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700479 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 mconsole_reply(req, "Bad configuration option", 1, 0);
481 return;
482 }
483
484 name = &ptr[strlen(dev->name)];
485 ptr = name;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700486 while ((*ptr != '=') && (*ptr != '\0'))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 ptr++;
488
Jeff Dikeae2587e2007-10-16 01:26:57 -0700489 if (*ptr == '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800490 err = (*dev->config)(name, &error_string);
491 mconsole_reply(req, error_string, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 }
493 else mconsole_get_config(dev->get_config, req, name);
494}
495
496void mconsole_remove(struct mc_request *req)
497{
Jeff Diked50084a2006-01-06 00:18:50 -0800498 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700499 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800500 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700501 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
503 ptr += strlen("remove");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800504 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700506 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 mconsole_reply(req, "Bad remove option", 1, 0);
508 return;
509 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700510
Jeff Dike3a331a52006-01-06 00:19:05 -0800511 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700512
Jeff Dike3a331a52006-01-06 00:19:05 -0800513 err = 1;
514 n = (*dev->id)(&ptr, &start, &end);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700515 if (n < 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800516 err_msg = "Couldn't parse device number";
517 goto out;
518 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700519 else if ((n < start) || (n > end)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800520 sprintf(error, "Invalid device number - must be between "
521 "%d and %d", start, end);
522 err_msg = error;
523 goto out;
524 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700525
Jeff Dikef28169d2007-02-10 01:43:53 -0800526 err_msg = NULL;
527 err = (*dev->remove)(n, &err_msg);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700528 switch(err) {
Jeff Diked40f6d72007-03-29 01:20:28 -0700529 case 0:
530 err_msg = "";
531 break;
Jeff Dike3a331a52006-01-06 00:19:05 -0800532 case -ENODEV:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700533 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800534 err_msg = "Device doesn't exist";
Jeff Dike3a331a52006-01-06 00:19:05 -0800535 break;
536 case -EBUSY:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700537 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800538 err_msg = "Device is currently open";
Jeff Dike3a331a52006-01-06 00:19:05 -0800539 break;
540 default:
541 break;
542 }
543out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700544 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545}
546
Jeff Dikef92afe52006-09-29 01:58:52 -0700547struct mconsole_output {
548 struct list_head list;
549 struct mc_request *req;
550};
551
Jeff Dike84f48d42007-02-10 01:44:01 -0800552static DEFINE_SPINLOCK(client_lock);
Jeff Dike6f517d32006-01-06 00:19:04 -0800553static LIST_HEAD(clients);
554static char console_buf[MCONSOLE_MAX_DATA];
Jeff Dike6f517d32006-01-06 00:19:04 -0800555
556static void console_write(struct console *console, const char *string,
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700557 unsigned int len)
Jeff Dike6f517d32006-01-06 00:19:04 -0800558{
559 struct list_head *ele;
560 int n;
561
Jeff Dikeae2587e2007-10-16 01:26:57 -0700562 if (list_empty(&clients))
Jeff Dike6f517d32006-01-06 00:19:04 -0800563 return;
564
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700565 while (len > 0) {
566 n = min((size_t) len, ARRAY_SIZE(console_buf));
567 strncpy(console_buf, string, n);
Jeff Dike6f517d32006-01-06 00:19:04 -0800568 string += n;
569 len -= n;
Jeff Dike6f517d32006-01-06 00:19:04 -0800570
Jeff Dikeae2587e2007-10-16 01:26:57 -0700571 list_for_each(ele, &clients) {
Jeff Dikef92afe52006-09-29 01:58:52 -0700572 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800573
Jeff Dikef92afe52006-09-29 01:58:52 -0700574 entry = list_entry(ele, struct mconsole_output, list);
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700575 mconsole_reply_len(entry->req, console_buf, n, 0, 1);
Jeff Dike6f517d32006-01-06 00:19:04 -0800576 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800577 }
578}
579
580static struct console mc_console = { .name = "mc",
581 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800582 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800583 .index = -1 };
584
585static int mc_add_console(void)
586{
587 register_console(&mc_console);
588 return 0;
589}
590
591late_initcall(mc_add_console);
592
593static void with_console(struct mc_request *req, void (*proc)(void *),
594 void *arg)
595{
Jeff Dikef92afe52006-09-29 01:58:52 -0700596 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800597 unsigned long flags;
598
Jeff Dikef92afe52006-09-29 01:58:52 -0700599 entry.req = req;
Jeff Dike84f48d42007-02-10 01:44:01 -0800600 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800601 list_add(&entry.list, &clients);
Jeff Dike84f48d42007-02-10 01:44:01 -0800602 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800603
604 (*proc)(arg);
605
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700606 mconsole_reply_len(req, "", 0, 0, 0);
Jeff Dike6f517d32006-01-06 00:19:04 -0800607
Jeff Dike84f48d42007-02-10 01:44:01 -0800608 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800609 list_del(&entry.list);
Jeff Dike84f48d42007-02-10 01:44:01 -0800610 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800611}
612
Jeff Dike4111b022006-01-06 00:19:05 -0800613#ifdef CONFIG_MAGIC_SYSRQ
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700614
615#include <linux/sysrq.h>
616
Jeff Dike4111b022006-01-06 00:19:05 -0800617static void sysrq_proc(void *arg)
618{
619 char *op = arg;
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700620 handle_sysrq(*op);
Jeff Dike4111b022006-01-06 00:19:05 -0800621}
622
623void mconsole_sysrq(struct mc_request *req)
624{
625 char *ptr = req->request.data;
626
627 ptr += strlen("sysrq");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800628 ptr = skip_spaces(ptr);
Jeff Dike4111b022006-01-06 00:19:05 -0800629
Jeff Dikeae2587e2007-10-16 01:26:57 -0700630 /*
631 * With 'b', the system will shut down without a chance to reply,
Jeff Dike4111b022006-01-06 00:19:05 -0800632 * so in this case, we reply first.
633 */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700634 if (*ptr == 'b')
Jeff Dike4111b022006-01-06 00:19:05 -0800635 mconsole_reply(req, "", 0, 0);
636
637 with_console(req, sysrq_proc, ptr);
638}
639#else
640void mconsole_sysrq(struct mc_request *req)
641{
642 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
643}
644#endif
645
Jeff Dike6f517d32006-01-06 00:19:04 -0800646static void stack_proc(void *arg)
647{
Richard Weinbergera1850e92013-09-23 17:38:03 +0200648 struct task_struct *task = arg;
Jeff Dike6f517d32006-01-06 00:19:04 -0800649
Richard Weinbergera1850e92013-09-23 17:38:03 +0200650 show_stack(task, NULL);
Jeff Dike6f517d32006-01-06 00:19:04 -0800651}
652
Jeff Dikeae2587e2007-10-16 01:26:57 -0700653/*
654 * Mconsole stack trace
Jeff Dike3eddddc2005-09-16 19:27:46 -0700655 * Added by Allan Graves, Jeff Dike
656 * Dumps a stacks registers to the linux console.
657 * Usage stack <pid>.
658 */
Jeff Dike42fda662007-10-16 01:26:50 -0700659void mconsole_stack(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700660{
Jeff Dike3a331a52006-01-06 00:19:05 -0800661 char *ptr = req->request.data;
662 int pid_requested= -1;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700663 struct task_struct *to = NULL;
664
Jeff Dikeae2587e2007-10-16 01:26:57 -0700665 /*
666 * Would be nice:
Jeff Dike3a331a52006-01-06 00:19:05 -0800667 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700668 * 2) Add a way to stack dump all pids.
669 */
670
Jeff Dike3a331a52006-01-06 00:19:05 -0800671 ptr += strlen("stack");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800672 ptr = skip_spaces(ptr);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700673
Jeff Dikeae2587e2007-10-16 01:26:57 -0700674 /*
675 * Should really check for multiple pids or reject bad args here
676 */
Jeff Dike3a331a52006-01-06 00:19:05 -0800677 /* What do the arguments in mconsole_reply mean? */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700678 if (sscanf(ptr, "%d", &pid_requested) == 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800679 mconsole_reply(req, "Please specify a pid", 1, 0);
680 return;
681 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700682
Jeff Dike827b3f62008-02-04 22:31:29 -0800683 to = find_task_by_pid_ns(pid_requested, &init_pid_ns);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700684 if ((to == NULL) || (pid_requested == 0)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800685 mconsole_reply(req, "Couldn't find that pid", 1, 0);
686 return;
687 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800688 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700689}
Jeff Dike3eddddc2005-09-16 19:27:46 -0700690
Jeff Dikeae2587e2007-10-16 01:26:57 -0700691/*
692 * Changed by mconsole_setup, which is __setup, and called before SMP is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 * active.
694 */
Jeff Diked50084a2006-01-06 00:18:50 -0800695static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700697static int __init mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698{
699 /* long to avoid size mismatch warnings from gcc */
700 long sock;
701 int err;
Balbir Singh36137122008-12-09 13:14:07 -0800702 char file[UNIX_PATH_MAX];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703
Jeff Dikeae2587e2007-10-16 01:26:57 -0700704 if (umid_file_name("mconsole", file, sizeof(file)))
705 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
707
708 sock = os_create_unix_socket(file, sizeof(file), 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700709 if (sock < 0) {
710 printk(KERN_ERR "Failed to initialize management console\n");
711 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 }
Jeff Dike438ee672008-02-04 22:31:19 -0800713 if (os_set_fd_block(sock, 0))
714 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715
716 register_reboot_notifier(&reboot_notifier);
717
718 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Theodore Ts'oaab94462012-07-17 14:18:23 -0400719 IRQF_SHARED, "mconsole", (void *)sock);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700720 if (err) {
721 printk(KERN_ERR "Failed to get IRQ for management console\n");
Jeff Dike438ee672008-02-04 22:31:19 -0800722 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 }
724
Jeff Dikeae2587e2007-10-16 01:26:57 -0700725 if (notify_socket != NULL) {
Jeff Dike970d6e32006-01-06 00:18:48 -0800726 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700727 if (notify_socket != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800729 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 strlen(mconsole_socket_name) + 1);
731 else printk(KERN_ERR "mconsole_setup failed to strdup "
732 "string\n");
733 }
734
Jeff Dikeae2587e2007-10-16 01:26:57 -0700735 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 MCONSOLE_VERSION, mconsole_socket_name);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700737 return 0;
Jeff Dike438ee672008-02-04 22:31:19 -0800738
739 out:
740 os_close_file(sock);
741 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742}
743
744__initcall(mconsole_init);
745
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800746static ssize_t mconsole_proc_write(struct file *file,
747 const char __user *buffer, size_t count, loff_t *pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748{
749 char *buf;
750
Al Viro793b7962016-01-02 14:53:28 -0500751 buf = memdup_user_nul(buffer, count);
752 if (IS_ERR(buf))
753 return PTR_ERR(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
755 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 kfree(buf);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700757 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758}
759
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800760static const struct file_operations mconsole_proc_fops = {
761 .owner = THIS_MODULE,
762 .write = mconsole_proc_write,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200763 .llseek = noop_llseek,
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800764};
765
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766static int create_proc_mconsole(void)
767{
768 struct proc_dir_entry *ent;
769
Jeff Dikeae2587e2007-10-16 01:26:57 -0700770 if (notify_socket == NULL)
771 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800773 ent = proc_create("mconsole", 0200, NULL, &mconsole_proc_fops);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700774 if (ent == NULL) {
David Howells4554eb92013-04-04 16:58:25 +0100775 printk(KERN_INFO "create_proc_mconsole : proc_create failed\n");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700776 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700778 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779}
780
781static DEFINE_SPINLOCK(notify_spinlock);
782
783void lock_notify(void)
784{
785 spin_lock(&notify_spinlock);
786}
787
788void unlock_notify(void)
789{
790 spin_unlock(&notify_spinlock);
791}
792
793__initcall(create_proc_mconsole);
794
Jeff Dike088bec42007-10-16 01:27:20 -0700795#define NOTIFY "notify:"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
797static int mconsole_setup(char *str)
798{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700799 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 str += strlen(NOTIFY);
801 notify_socket = str;
802 }
803 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700804 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805}
806
Jeff Dike088bec42007-10-16 01:27:20 -0700807__setup("mconsole=", mconsole_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808
809__uml_help(mconsole_setup,
810"mconsole=notify:<socket>\n"
811" Requests that the mconsole driver send a message to the named Unix\n"
812" socket containing the name of the mconsole socket. This also serves\n"
813" to notify outside processes when UML has booted far enough to respond\n"
814" to mconsole requests.\n\n"
815);
816
817static int notify_panic(struct notifier_block *self, unsigned long unused1,
818 void *ptr)
819{
820 char *message = ptr;
821
Jeff Dikeae2587e2007-10-16 01:26:57 -0700822 if (notify_socket == NULL)
823 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824
Jeff Diked50084a2006-01-06 00:18:50 -0800825 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 strlen(message) + 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700827 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828}
829
830static struct notifier_block panic_exit_notifier = {
831 .notifier_call = notify_panic,
832 .next = NULL,
833 .priority = 1
834};
835
836static int add_notifier(void)
837{
Alan Sterne041c682006-03-27 01:16:30 -0800838 atomic_notifier_chain_register(&panic_notifier_list,
839 &panic_exit_notifier);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700840 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841}
842
843__initcall(add_notifier);
844
845char *mconsole_notify_socket(void)
846{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700847 return notify_socket;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848}
849
850EXPORT_SYMBOL(mconsole_notify_socket);