blob: c4d162a94be9d612d6ec834c243b4626a6b15779 [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>
Ingo Molnarb17b0152017-02-08 18:51:35 +010016#include <linux/sched/debug.h>
Jeff Dike827b3f62008-02-04 22:31:29 -080017#include <linux/proc_fs.h>
18#include <linux/slab.h>
19#include <linux/syscalls.h>
20#include <linux/utsname.h>
Balbir Singh36137122008-12-09 13:14:07 -080021#include <linux/socket.h>
22#include <linux/un.h>
Jeff Dike827b3f62008-02-04 22:31:29 -080023#include <linux/workqueue.h>
24#include <linux/mutex.h>
Al Viro723a1d72012-08-19 13:00:49 -040025#include <linux/fs.h>
26#include <linux/mount.h>
27#include <linux/file.h>
Linus Torvalds7c0f6ba2016-12-24 11:46:01 -080028#include <linux/uaccess.h>
Richard Weinbergera3a85a72012-03-29 18:47:46 +020029#include <asm/switch_to.h>
Jeff Dike827b3f62008-02-04 22:31:29 -080030
Al Viro37185b32012-10-08 03:27:32 +010031#include <init.h>
32#include <irq_kern.h>
33#include <irq_user.h>
34#include <kern_util.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include "mconsole.h"
36#include "mconsole_kern.h"
Al Viro37185b32012-10-08 03:27:32 +010037#include <os.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070038
Jeff Diked50084a2006-01-06 00:18:50 -080039static int do_unlink_socket(struct notifier_block *notifier,
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 unsigned long what, void *data)
41{
Jeff Dikeae2587e2007-10-16 01:26:57 -070042 return mconsole_unlink_socket();
Linus Torvalds1da177e2005-04-16 15:20:36 -070043}
44
45
46static struct notifier_block reboot_notifier = {
47 .notifier_call = do_unlink_socket,
48 .priority = 0,
49};
50
Jeff Diked50084a2006-01-06 00:18:50 -080051/* Safe without explicit locking for now. Tasklets provide their own
Linus Torvalds1da177e2005-04-16 15:20:36 -070052 * locking, and the interrupt handler is safe because it can't interrupt
53 * itself and it can only happen on CPU 0.
54 */
55
Jeff Dike90107722006-01-06 00:18:54 -080056static LIST_HEAD(mc_requests);
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
David Howells6d5aefb2006-12-05 19:36:26 +000058static void mc_work_proc(struct work_struct *unused)
Linus Torvalds1da177e2005-04-16 15:20:36 -070059{
60 struct mconsole_entry *req;
61 unsigned long flags;
62
Jeff Dikeae2587e2007-10-16 01:26:57 -070063 while (!list_empty(&mc_requests)) {
Paolo 'Blaisorblade' Giarrussodbdb4c02006-04-10 22:53:39 -070064 local_irq_save(flags);
Jeff Dikeae2587e2007-10-16 01:26:57 -070065 req = list_entry(mc_requests.next, struct mconsole_entry, list);
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 list_del(&req->list);
67 local_irq_restore(flags);
68 req->request.cmd->handler(&req->request);
69 kfree(req);
70 }
71}
72
David Howells6d5aefb2006-12-05 19:36:26 +000073static DECLARE_WORK(mconsole_work, mc_work_proc);
Linus Torvalds1da177e2005-04-16 15:20:36 -070074
Al Viro7bea96f2006-10-08 22:49:34 +010075static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070076{
77 /* long to avoid size mismatch warnings from gcc */
78 long fd;
79 struct mconsole_entry *new;
Al Viro3a512372006-10-24 11:15:29 +010080 static struct mc_request req; /* that's OK */
Linus Torvalds1da177e2005-04-16 15:20:36 -070081
82 fd = (long) dev_id;
Jeff Dikeae2587e2007-10-16 01:26:57 -070083 while (mconsole_get_request(fd, &req)) {
84 if (req.cmd->context == MCONSOLE_INTR)
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 (*req.cmd->handler)(&req);
86 else {
Jeff Dike60baa152006-04-10 22:53:28 -070087 new = kmalloc(sizeof(*new), GFP_NOWAIT);
Jeff Dikeae2587e2007-10-16 01:26:57 -070088 if (new == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 mconsole_reply(&req, "Out of memory", 1, 0);
90 else {
91 new->request = req;
Al Viro3a512372006-10-24 11:15:29 +010092 new->request.regs = get_irq_regs()->regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 list_add(&new->list, &mc_requests);
94 }
95 }
96 }
Jeff Dikeae2587e2007-10-16 01:26:57 -070097 if (!list_empty(&mc_requests))
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 schedule_work(&mconsole_work);
99 reactivate_fd(fd, MCONSOLE_IRQ);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700100 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101}
102
103void mconsole_version(struct mc_request *req)
104{
105 char version[256];
106
Serge E. Hallyne9ff3992006-10-02 02:18:11 -0700107 sprintf(version, "%s %s %s %s %s", utsname()->sysname,
Jeff Dikeae2587e2007-10-16 01:26:57 -0700108 utsname()->nodename, utsname()->release, utsname()->version,
109 utsname()->machine);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 mconsole_reply(req, version, 0, 0);
111}
112
113void mconsole_log(struct mc_request *req)
114{
115 int len;
116 char *ptr = req->request.data;
117
118 ptr += strlen("log ");
119
120 len = req->len - (ptr - req->request.data);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700121 printk(KERN_WARNING "%.*s", len, ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 mconsole_reply(req, "", 0, 0);
123}
124
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125void mconsole_proc(struct mc_request *req)
126{
Eric W. Biederman17cf22c2010-03-02 14:51:53 -0800127 struct vfsmount *mnt = task_active_pid_ns(current)->proc_mnt;
Al Viro723a1d72012-08-19 13:00:49 -0400128 char *buf;
129 int len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 struct file *file;
Al Viro723a1d72012-08-19 13:00:49 -0400131 int first_chunk = 1;
132 char *ptr = req->request.data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133
134 ptr += strlen("proc");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800135 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136
Jann Horn378c6522016-03-22 14:25:36 -0700137 file = file_open_root(mnt->mnt_root, mnt, ptr, O_RDONLY, 0);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700138 if (IS_ERR(file)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 mconsole_reply(req, "Failed to open file", 1, 0);
Al Viro723a1d72012-08-19 13:00:49 -0400140 printk(KERN_ERR "open /proc/%s: %ld\n", ptr, PTR_ERR(file));
Al Viro4ecf09f2009-12-24 00:44:44 -0500141 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
144 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700145 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
147 goto out_fput;
148 }
149
Al Viro723a1d72012-08-19 13:00:49 -0400150 do {
Christoph Hellwig21cb2552017-09-01 17:39:20 +0200151 len = kernel_read(file, buf, PAGE_SIZE - 1, &file->f_pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 if (len < 0) {
153 mconsole_reply(req, "Read of file failed", 1, 0);
154 goto out_free;
155 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700156 /* Begin the file content on his own line. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 if (first_chunk) {
158 mconsole_reply(req, "\n", 0, 1);
159 first_chunk = 0;
160 }
Al Viro723a1d72012-08-19 13:00:49 -0400161 buf[len] = '\0';
162 mconsole_reply(req, buf, 0, (len > 0));
163 } while (len > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 out_free:
165 kfree(buf);
Al Viro723a1d72012-08-19 13:00:49 -0400166 out_fput:
167 fput(file);
168 out: ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169}
170
171#define UML_MCONSOLE_HELPTEXT \
172"Commands: \n\
173 version - Get kernel version \n\
174 help - Print this message \n\
175 halt - Halt UML \n\
176 reboot - Reboot UML \n\
177 config <dev>=<config> - Add a new device to UML; \n\
178 same syntax as command line \n\
179 config <dev> - Query the configuration of a device \n\
180 remove <dev> - Remove a device from UML \n\
181 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
Jeff Dikedb805812006-02-01 03:06:23 -0800182 cad - invoke the Ctrl-Alt-Del handler \n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 stop - pause the UML; it will do nothing until it receives a 'go' \n\
184 go - continue the UML after a 'stop' \n\
185 log <string> - make UML enter <string> into the kernel log\n\
186 proc <file> - returns the contents of the UML's /proc/<file>\n\
Jeff Dike3eddddc2005-09-16 19:27:46 -0700187 stack <pid> - returns the stack of the specified pid\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188"
189
190void mconsole_help(struct mc_request *req)
191{
192 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
193}
194
195void mconsole_halt(struct mc_request *req)
196{
197 mconsole_reply(req, "", 0, 0);
198 machine_halt();
199}
200
201void mconsole_reboot(struct mc_request *req)
202{
203 mconsole_reply(req, "", 0, 0);
204 machine_restart(NULL);
205}
206
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207void mconsole_cad(struct mc_request *req)
208{
209 mconsole_reply(req, "", 0, 0);
210 ctrl_alt_del();
211}
212
213void mconsole_go(struct mc_request *req)
214{
215 mconsole_reply(req, "Not stopped", 1, 0);
216}
217
218void mconsole_stop(struct mc_request *req)
219{
220 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
221 os_set_fd_block(req->originating_fd, 1);
Al Viro3a512372006-10-24 11:15:29 +0100222 mconsole_reply(req, "stopped", 0, 0);
Karol Swietlickicc0be0f2008-02-04 22:31:25 -0800223 for (;;) {
224 if (!mconsole_get_request(req->originating_fd, req))
225 continue;
Al Viro3a512372006-10-24 11:15:29 +0100226 if (req->cmd->handler == mconsole_go)
227 break;
228 if (req->cmd->handler == mconsole_stop) {
229 mconsole_reply(req, "Already stopped", 1, 0);
230 continue;
231 }
232 if (req->cmd->handler == mconsole_sysrq) {
233 struct pt_regs *old_regs;
234 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
235 mconsole_sysrq(req);
236 set_irq_regs(old_regs);
237 continue;
238 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 (*req->cmd->handler)(req);
240 }
241 os_set_fd_block(req->originating_fd, 0);
242 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
243 mconsole_reply(req, "", 0, 0);
244}
245
Jeff Dike84f48d42007-02-10 01:44:01 -0800246static DEFINE_SPINLOCK(mc_devices_lock);
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800247static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
249void mconsole_register_dev(struct mc_device *new)
250{
Jeff Dike84f48d42007-02-10 01:44:01 -0800251 spin_lock(&mc_devices_lock);
252 BUG_ON(!list_empty(&new->list));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 list_add(&new->list, &mconsole_devices);
Jeff Dike84f48d42007-02-10 01:44:01 -0800254 spin_unlock(&mc_devices_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255}
256
257static struct mc_device *mconsole_find_dev(char *name)
258{
259 struct list_head *ele;
260 struct mc_device *dev;
261
Jeff Dikeae2587e2007-10-16 01:26:57 -0700262 list_for_each(ele, &mconsole_devices) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 dev = list_entry(ele, struct mc_device, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700264 if (!strncmp(name, dev->name, strlen(dev->name)))
265 return dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700267 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268}
269
Jeff Dike02dea082006-03-31 02:30:08 -0800270#define UNPLUGGED_PER_PAGE \
271 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
272
273struct unplugged_pages {
274 struct list_head list;
275 void *pages[UNPLUGGED_PER_PAGE];
276};
277
Daniel Walkere98fa282008-02-04 22:31:27 -0800278static DEFINE_MUTEX(plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800279static unsigned long long unplugged_pages_count = 0;
Jeff Dikec59bce62007-02-10 01:44:04 -0800280static LIST_HEAD(unplugged_pages);
Jeff Dike02dea082006-03-31 02:30:08 -0800281static int unplug_index = UNPLUGGED_PER_PAGE;
282
Jeff Dikef28169d2007-02-10 01:43:53 -0800283static int mem_config(char *str, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800284{
285 unsigned long long diff;
286 int err = -EINVAL, i, add;
287 char *ret;
288
Jeff Dikeae2587e2007-10-16 01:26:57 -0700289 if (str[0] != '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800290 *error_out = "Expected '=' after 'mem'";
Jeff Dike02dea082006-03-31 02:30:08 -0800291 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800292 }
Jeff Dike02dea082006-03-31 02:30:08 -0800293
294 str++;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700295 if (str[0] == '-')
Jeff Dike02dea082006-03-31 02:30:08 -0800296 add = 0;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700297 else if (str[0] == '+') {
Jeff Dike02dea082006-03-31 02:30:08 -0800298 add = 1;
299 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800300 else {
301 *error_out = "Expected increment to start with '-' or '+'";
302 goto out;
303 }
Jeff Dike02dea082006-03-31 02:30:08 -0800304
305 str++;
306 diff = memparse(str, &ret);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700307 if (*ret != '\0') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800308 *error_out = "Failed to parse memory increment";
Jeff Dike02dea082006-03-31 02:30:08 -0800309 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800310 }
Jeff Dike02dea082006-03-31 02:30:08 -0800311
312 diff /= PAGE_SIZE;
313
Daniel Walkere98fa282008-02-04 22:31:27 -0800314 mutex_lock(&plug_mem_mutex);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700315 for (i = 0; i < diff; i++) {
Jeff Dike02dea082006-03-31 02:30:08 -0800316 struct unplugged_pages *unplugged;
317 void *addr;
318
Jeff Dikeae2587e2007-10-16 01:26:57 -0700319 if (add) {
320 if (list_empty(&unplugged_pages))
Jeff Dike02dea082006-03-31 02:30:08 -0800321 break;
322
323 unplugged = list_entry(unplugged_pages.next,
324 struct unplugged_pages, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700325 if (unplug_index > 0)
Jeff Dike02dea082006-03-31 02:30:08 -0800326 addr = unplugged->pages[--unplug_index];
327 else {
328 list_del(&unplugged->list);
329 addr = unplugged;
330 unplug_index = UNPLUGGED_PER_PAGE;
331 }
332
333 free_page((unsigned long) addr);
334 unplugged_pages_count--;
335 }
336 else {
337 struct page *page;
338
339 page = alloc_page(GFP_ATOMIC);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700340 if (page == NULL)
Jeff Dike02dea082006-03-31 02:30:08 -0800341 break;
342
343 unplugged = page_address(page);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700344 if (unplug_index == UNPLUGGED_PER_PAGE) {
Jeff Dike02dea082006-03-31 02:30:08 -0800345 list_add(&unplugged->list, &unplugged_pages);
346 unplug_index = 0;
347 }
348 else {
349 struct list_head *entry = unplugged_pages.next;
350 addr = unplugged;
351
352 unplugged = list_entry(entry,
353 struct unplugged_pages,
354 list);
Jeff Dike02dea082006-03-31 02:30:08 -0800355 err = os_drop_memory(addr, PAGE_SIZE);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700356 if (err) {
357 printk(KERN_ERR "Failed to release "
358 "memory - errno = %d\n", err);
Jeff Dikef28169d2007-02-10 01:43:53 -0800359 *error_out = "Failed to release memory";
Jeff Dike84f48d42007-02-10 01:44:01 -0800360 goto out_unlock;
Jeff Dikef28169d2007-02-10 01:43:53 -0800361 }
362 unplugged->pages[unplug_index++] = addr;
Jeff Dike02dea082006-03-31 02:30:08 -0800363 }
364
365 unplugged_pages_count++;
366 }
367 }
368
369 err = 0;
Jeff Dike84f48d42007-02-10 01:44:01 -0800370out_unlock:
Daniel Walkere98fa282008-02-04 22:31:27 -0800371 mutex_unlock(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800372out:
373 return err;
374}
375
376static int mem_get_config(char *name, char *str, int size, char **error_out)
377{
378 char buf[sizeof("18446744073709551615")];
379 int len = 0;
380
381 sprintf(buf, "%ld", uml_physmem);
382 CONFIG_CHUNK(str, size, len, buf, 1);
383
384 return len;
385}
386
387static int mem_id(char **str, int *start_out, int *end_out)
388{
389 *start_out = 0;
390 *end_out = 0;
391
392 return 0;
393}
394
Jeff Dikef28169d2007-02-10 01:43:53 -0800395static int mem_remove(int n, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800396{
Jeff Dikef28169d2007-02-10 01:43:53 -0800397 *error_out = "Memory doesn't support the remove operation";
Jeff Dike02dea082006-03-31 02:30:08 -0800398 return -EBUSY;
399}
400
401static struct mc_device mem_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800402 .list = LIST_HEAD_INIT(mem_mc.list),
Jeff Dike02dea082006-03-31 02:30:08 -0800403 .name = "mem",
404 .config = mem_config,
405 .get_config = mem_get_config,
406 .id = mem_id,
407 .remove = mem_remove,
408};
409
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700410static int __init mem_mc_init(void)
Jeff Dike02dea082006-03-31 02:30:08 -0800411{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700412 if (can_drop_memory())
Jeff Dike02dea082006-03-31 02:30:08 -0800413 mconsole_register_dev(&mem_mc);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700414 else printk(KERN_ERR "Can't release memory to the host - memory "
415 "hotplug won't be supported\n");
Jeff Dike02dea082006-03-31 02:30:08 -0800416 return 0;
417}
418
419__initcall(mem_mc_init);
420
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421#define CONFIG_BUF_SIZE 64
422
Jeff Diked50084a2006-01-06 00:18:50 -0800423static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 char **),
425 struct mc_request *req, char *name)
426{
427 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
428 int n, size;
429
Jeff Dikeae2587e2007-10-16 01:26:57 -0700430 if (get_config == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 mconsole_reply(req, "No get_config routine defined", 1, 0);
432 return;
433 }
434
435 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700436 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 buf = default_buf;
438
Jeff Dikeae2587e2007-10-16 01:26:57 -0700439 while (1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 n = (*get_config)(name, buf, size, &error);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700441 if (error != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 mconsole_reply(req, error, 1, 0);
443 goto out;
444 }
445
Jeff Dikeae2587e2007-10-16 01:26:57 -0700446 if (n <= size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 mconsole_reply(req, buf, 0, 0);
448 goto out;
449 }
450
Jeff Dikeae2587e2007-10-16 01:26:57 -0700451 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 kfree(buf);
453
454 size = n;
455 buf = kmalloc(size, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700456 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
458 return;
459 }
460 }
461 out:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700462 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464}
465
466void mconsole_config(struct mc_request *req)
467{
468 struct mc_device *dev;
Jeff Dikef28169d2007-02-10 01:43:53 -0800469 char *ptr = req->request.data, *name, *error_string = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 int err;
471
472 ptr += strlen("config");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800473 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700475 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 mconsole_reply(req, "Bad configuration option", 1, 0);
477 return;
478 }
479
480 name = &ptr[strlen(dev->name)];
481 ptr = name;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700482 while ((*ptr != '=') && (*ptr != '\0'))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 ptr++;
484
Jeff Dikeae2587e2007-10-16 01:26:57 -0700485 if (*ptr == '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800486 err = (*dev->config)(name, &error_string);
487 mconsole_reply(req, error_string, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 }
489 else mconsole_get_config(dev->get_config, req, name);
490}
491
492void mconsole_remove(struct mc_request *req)
493{
Jeff Diked50084a2006-01-06 00:18:50 -0800494 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700495 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800496 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700497 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
499 ptr += strlen("remove");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800500 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700502 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 mconsole_reply(req, "Bad remove option", 1, 0);
504 return;
505 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700506
Jeff Dike3a331a52006-01-06 00:19:05 -0800507 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700508
Jeff Dike3a331a52006-01-06 00:19:05 -0800509 err = 1;
510 n = (*dev->id)(&ptr, &start, &end);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700511 if (n < 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800512 err_msg = "Couldn't parse device number";
513 goto out;
514 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700515 else if ((n < start) || (n > end)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800516 sprintf(error, "Invalid device number - must be between "
517 "%d and %d", start, end);
518 err_msg = error;
519 goto out;
520 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700521
Jeff Dikef28169d2007-02-10 01:43:53 -0800522 err_msg = NULL;
523 err = (*dev->remove)(n, &err_msg);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700524 switch(err) {
Jeff Diked40f6d72007-03-29 01:20:28 -0700525 case 0:
526 err_msg = "";
527 break;
Jeff Dike3a331a52006-01-06 00:19:05 -0800528 case -ENODEV:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700529 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800530 err_msg = "Device doesn't exist";
Jeff Dike3a331a52006-01-06 00:19:05 -0800531 break;
532 case -EBUSY:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700533 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800534 err_msg = "Device is currently open";
Jeff Dike3a331a52006-01-06 00:19:05 -0800535 break;
536 default:
537 break;
538 }
539out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700540 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541}
542
Jeff Dikef92afe52006-09-29 01:58:52 -0700543struct mconsole_output {
544 struct list_head list;
545 struct mc_request *req;
546};
547
Jeff Dike84f48d42007-02-10 01:44:01 -0800548static DEFINE_SPINLOCK(client_lock);
Jeff Dike6f517d32006-01-06 00:19:04 -0800549static LIST_HEAD(clients);
550static char console_buf[MCONSOLE_MAX_DATA];
Jeff Dike6f517d32006-01-06 00:19:04 -0800551
552static void console_write(struct console *console, const char *string,
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700553 unsigned int len)
Jeff Dike6f517d32006-01-06 00:19:04 -0800554{
555 struct list_head *ele;
556 int n;
557
Jeff Dikeae2587e2007-10-16 01:26:57 -0700558 if (list_empty(&clients))
Jeff Dike6f517d32006-01-06 00:19:04 -0800559 return;
560
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700561 while (len > 0) {
562 n = min((size_t) len, ARRAY_SIZE(console_buf));
563 strncpy(console_buf, string, n);
Jeff Dike6f517d32006-01-06 00:19:04 -0800564 string += n;
565 len -= n;
Jeff Dike6f517d32006-01-06 00:19:04 -0800566
Jeff Dikeae2587e2007-10-16 01:26:57 -0700567 list_for_each(ele, &clients) {
Jeff Dikef92afe52006-09-29 01:58:52 -0700568 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800569
Jeff Dikef92afe52006-09-29 01:58:52 -0700570 entry = list_entry(ele, struct mconsole_output, list);
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700571 mconsole_reply_len(entry->req, console_buf, n, 0, 1);
Jeff Dike6f517d32006-01-06 00:19:04 -0800572 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800573 }
574}
575
576static struct console mc_console = { .name = "mc",
577 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800578 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800579 .index = -1 };
580
581static int mc_add_console(void)
582{
583 register_console(&mc_console);
584 return 0;
585}
586
587late_initcall(mc_add_console);
588
589static void with_console(struct mc_request *req, void (*proc)(void *),
590 void *arg)
591{
Jeff Dikef92afe52006-09-29 01:58:52 -0700592 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800593 unsigned long flags;
594
Jeff Dikef92afe52006-09-29 01:58:52 -0700595 entry.req = req;
Jeff Dike84f48d42007-02-10 01:44:01 -0800596 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800597 list_add(&entry.list, &clients);
Jeff Dike84f48d42007-02-10 01:44:01 -0800598 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800599
600 (*proc)(arg);
601
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700602 mconsole_reply_len(req, "", 0, 0, 0);
Jeff Dike6f517d32006-01-06 00:19:04 -0800603
Jeff Dike84f48d42007-02-10 01:44:01 -0800604 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800605 list_del(&entry.list);
Jeff Dike84f48d42007-02-10 01:44:01 -0800606 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800607}
608
Jeff Dike4111b022006-01-06 00:19:05 -0800609#ifdef CONFIG_MAGIC_SYSRQ
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700610
611#include <linux/sysrq.h>
612
Jeff Dike4111b022006-01-06 00:19:05 -0800613static void sysrq_proc(void *arg)
614{
615 char *op = arg;
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700616 handle_sysrq(*op);
Jeff Dike4111b022006-01-06 00:19:05 -0800617}
618
619void mconsole_sysrq(struct mc_request *req)
620{
621 char *ptr = req->request.data;
622
623 ptr += strlen("sysrq");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800624 ptr = skip_spaces(ptr);
Jeff Dike4111b022006-01-06 00:19:05 -0800625
Jeff Dikeae2587e2007-10-16 01:26:57 -0700626 /*
627 * With 'b', the system will shut down without a chance to reply,
Jeff Dike4111b022006-01-06 00:19:05 -0800628 * so in this case, we reply first.
629 */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700630 if (*ptr == 'b')
Jeff Dike4111b022006-01-06 00:19:05 -0800631 mconsole_reply(req, "", 0, 0);
632
633 with_console(req, sysrq_proc, ptr);
634}
635#else
636void mconsole_sysrq(struct mc_request *req)
637{
638 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
639}
640#endif
641
Jeff Dike6f517d32006-01-06 00:19:04 -0800642static void stack_proc(void *arg)
643{
Richard Weinbergera1850e92013-09-23 17:38:03 +0200644 struct task_struct *task = arg;
Jeff Dike6f517d32006-01-06 00:19:04 -0800645
Richard Weinbergera1850e92013-09-23 17:38:03 +0200646 show_stack(task, NULL);
Jeff Dike6f517d32006-01-06 00:19:04 -0800647}
648
Jeff Dikeae2587e2007-10-16 01:26:57 -0700649/*
650 * Mconsole stack trace
Jeff Dike3eddddc2005-09-16 19:27:46 -0700651 * Added by Allan Graves, Jeff Dike
652 * Dumps a stacks registers to the linux console.
653 * Usage stack <pid>.
654 */
Jeff Dike42fda662007-10-16 01:26:50 -0700655void mconsole_stack(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700656{
Jeff Dike3a331a52006-01-06 00:19:05 -0800657 char *ptr = req->request.data;
658 int pid_requested= -1;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700659 struct task_struct *to = NULL;
660
Jeff Dikeae2587e2007-10-16 01:26:57 -0700661 /*
662 * Would be nice:
Jeff Dike3a331a52006-01-06 00:19:05 -0800663 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700664 * 2) Add a way to stack dump all pids.
665 */
666
Jeff Dike3a331a52006-01-06 00:19:05 -0800667 ptr += strlen("stack");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800668 ptr = skip_spaces(ptr);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700669
Jeff Dikeae2587e2007-10-16 01:26:57 -0700670 /*
671 * Should really check for multiple pids or reject bad args here
672 */
Jeff Dike3a331a52006-01-06 00:19:05 -0800673 /* What do the arguments in mconsole_reply mean? */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700674 if (sscanf(ptr, "%d", &pid_requested) == 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800675 mconsole_reply(req, "Please specify a pid", 1, 0);
676 return;
677 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700678
Jeff Dike827b3f62008-02-04 22:31:29 -0800679 to = find_task_by_pid_ns(pid_requested, &init_pid_ns);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700680 if ((to == NULL) || (pid_requested == 0)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800681 mconsole_reply(req, "Couldn't find that pid", 1, 0);
682 return;
683 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800684 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700685}
Jeff Dike3eddddc2005-09-16 19:27:46 -0700686
Jeff Dikeae2587e2007-10-16 01:26:57 -0700687/*
688 * Changed by mconsole_setup, which is __setup, and called before SMP is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 * active.
690 */
Jeff Diked50084a2006-01-06 00:18:50 -0800691static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700693static int __init mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694{
695 /* long to avoid size mismatch warnings from gcc */
696 long sock;
697 int err;
Balbir Singh36137122008-12-09 13:14:07 -0800698 char file[UNIX_PATH_MAX];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699
Jeff Dikeae2587e2007-10-16 01:26:57 -0700700 if (umid_file_name("mconsole", file, sizeof(file)))
701 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
703
704 sock = os_create_unix_socket(file, sizeof(file), 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700705 if (sock < 0) {
706 printk(KERN_ERR "Failed to initialize management console\n");
707 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 }
Jeff Dike438ee672008-02-04 22:31:19 -0800709 if (os_set_fd_block(sock, 0))
710 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711
712 register_reboot_notifier(&reboot_notifier);
713
714 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Theodore Ts'oaab94462012-07-17 14:18:23 -0400715 IRQF_SHARED, "mconsole", (void *)sock);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700716 if (err) {
717 printk(KERN_ERR "Failed to get IRQ for management console\n");
Jeff Dike438ee672008-02-04 22:31:19 -0800718 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 }
720
Jeff Dikeae2587e2007-10-16 01:26:57 -0700721 if (notify_socket != NULL) {
Jeff Dike970d6e32006-01-06 00:18:48 -0800722 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700723 if (notify_socket != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800725 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 strlen(mconsole_socket_name) + 1);
727 else printk(KERN_ERR "mconsole_setup failed to strdup "
728 "string\n");
729 }
730
Jeff Dikeae2587e2007-10-16 01:26:57 -0700731 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 MCONSOLE_VERSION, mconsole_socket_name);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700733 return 0;
Jeff Dike438ee672008-02-04 22:31:19 -0800734
735 out:
736 os_close_file(sock);
737 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738}
739
740__initcall(mconsole_init);
741
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800742static ssize_t mconsole_proc_write(struct file *file,
743 const char __user *buffer, size_t count, loff_t *pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744{
745 char *buf;
746
Al Viro793b7962016-01-02 14:53:28 -0500747 buf = memdup_user_nul(buffer, count);
748 if (IS_ERR(buf))
749 return PTR_ERR(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
751 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 kfree(buf);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700753 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754}
755
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800756static const struct file_operations mconsole_proc_fops = {
757 .owner = THIS_MODULE,
758 .write = mconsole_proc_write,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200759 .llseek = noop_llseek,
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800760};
761
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762static int create_proc_mconsole(void)
763{
764 struct proc_dir_entry *ent;
765
Jeff Dikeae2587e2007-10-16 01:26:57 -0700766 if (notify_socket == NULL)
767 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800769 ent = proc_create("mconsole", 0200, NULL, &mconsole_proc_fops);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700770 if (ent == NULL) {
David Howells4554eb92013-04-04 16:58:25 +0100771 printk(KERN_INFO "create_proc_mconsole : proc_create failed\n");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700772 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700774 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775}
776
777static DEFINE_SPINLOCK(notify_spinlock);
778
779void lock_notify(void)
780{
781 spin_lock(&notify_spinlock);
782}
783
784void unlock_notify(void)
785{
786 spin_unlock(&notify_spinlock);
787}
788
789__initcall(create_proc_mconsole);
790
Jeff Dike088bec42007-10-16 01:27:20 -0700791#define NOTIFY "notify:"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792
793static int mconsole_setup(char *str)
794{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700795 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 str += strlen(NOTIFY);
797 notify_socket = str;
798 }
799 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700800 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801}
802
Jeff Dike088bec42007-10-16 01:27:20 -0700803__setup("mconsole=", mconsole_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804
805__uml_help(mconsole_setup,
806"mconsole=notify:<socket>\n"
807" Requests that the mconsole driver send a message to the named Unix\n"
808" socket containing the name of the mconsole socket. This also serves\n"
809" to notify outside processes when UML has booted far enough to respond\n"
810" to mconsole requests.\n\n"
811);
812
813static int notify_panic(struct notifier_block *self, unsigned long unused1,
814 void *ptr)
815{
816 char *message = ptr;
817
Jeff Dikeae2587e2007-10-16 01:26:57 -0700818 if (notify_socket == NULL)
819 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820
Jeff Diked50084a2006-01-06 00:18:50 -0800821 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 strlen(message) + 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700823 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824}
825
826static struct notifier_block panic_exit_notifier = {
827 .notifier_call = notify_panic,
828 .next = NULL,
829 .priority = 1
830};
831
832static int add_notifier(void)
833{
Alan Sterne041c682006-03-27 01:16:30 -0800834 atomic_notifier_chain_register(&panic_notifier_list,
835 &panic_exit_notifier);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700836 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837}
838
839__initcall(add_notifier);
840
841char *mconsole_notify_socket(void)
842{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700843 return notify_socket;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844}
845
846EXPORT_SYMBOL(mconsole_notify_socket);