blob: d5f9a2d1da1ba8f8e667e3fc781db1a5136432af [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;
Al Viro7a5016092018-02-10 01:35:16 +0000133 loff_t pos = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134
135 ptr += strlen("proc");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800136 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137
Jann Horn378c6522016-03-22 14:25:36 -0700138 file = file_open_root(mnt->mnt_root, mnt, ptr, O_RDONLY, 0);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700139 if (IS_ERR(file)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 mconsole_reply(req, "Failed to open file", 1, 0);
Al Viro723a1d72012-08-19 13:00:49 -0400141 printk(KERN_ERR "open /proc/%s: %ld\n", ptr, PTR_ERR(file));
Al Viro4ecf09f2009-12-24 00:44:44 -0500142 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144
145 buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700146 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
148 goto out_fput;
149 }
150
Al Viro723a1d72012-08-19 13:00:49 -0400151 do {
Al Viro7a5016092018-02-10 01:35:16 +0000152 len = kernel_read(file, buf, PAGE_SIZE - 1, &pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 if (len < 0) {
154 mconsole_reply(req, "Read of file failed", 1, 0);
155 goto out_free;
156 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700157 /* Begin the file content on his own line. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 if (first_chunk) {
159 mconsole_reply(req, "\n", 0, 1);
160 first_chunk = 0;
161 }
Al Viro723a1d72012-08-19 13:00:49 -0400162 buf[len] = '\0';
163 mconsole_reply(req, buf, 0, (len > 0));
164 } while (len > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 out_free:
166 kfree(buf);
Al Viro723a1d72012-08-19 13:00:49 -0400167 out_fput:
168 fput(file);
169 out: ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170}
171
172#define UML_MCONSOLE_HELPTEXT \
173"Commands: \n\
174 version - Get kernel version \n\
175 help - Print this message \n\
176 halt - Halt UML \n\
177 reboot - Reboot UML \n\
178 config <dev>=<config> - Add a new device to UML; \n\
179 same syntax as command line \n\
180 config <dev> - Query the configuration of a device \n\
181 remove <dev> - Remove a device from UML \n\
182 sysrq <letter> - Performs the SysRq action controlled by the letter \n\
Jeff Dikedb805812006-02-01 03:06:23 -0800183 cad - invoke the Ctrl-Alt-Del handler \n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 stop - pause the UML; it will do nothing until it receives a 'go' \n\
185 go - continue the UML after a 'stop' \n\
186 log <string> - make UML enter <string> into the kernel log\n\
187 proc <file> - returns the contents of the UML's /proc/<file>\n\
Jeff Dike3eddddc2005-09-16 19:27:46 -0700188 stack <pid> - returns the stack of the specified pid\n\
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189"
190
191void mconsole_help(struct mc_request *req)
192{
193 mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
194}
195
196void mconsole_halt(struct mc_request *req)
197{
198 mconsole_reply(req, "", 0, 0);
199 machine_halt();
200}
201
202void mconsole_reboot(struct mc_request *req)
203{
204 mconsole_reply(req, "", 0, 0);
205 machine_restart(NULL);
206}
207
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208void mconsole_cad(struct mc_request *req)
209{
210 mconsole_reply(req, "", 0, 0);
211 ctrl_alt_del();
212}
213
214void mconsole_go(struct mc_request *req)
215{
216 mconsole_reply(req, "Not stopped", 1, 0);
217}
218
219void mconsole_stop(struct mc_request *req)
220{
221 deactivate_fd(req->originating_fd, MCONSOLE_IRQ);
222 os_set_fd_block(req->originating_fd, 1);
Al Viro3a512372006-10-24 11:15:29 +0100223 mconsole_reply(req, "stopped", 0, 0);
Karol Swietlickicc0be0f2008-02-04 22:31:25 -0800224 for (;;) {
225 if (!mconsole_get_request(req->originating_fd, req))
226 continue;
Al Viro3a512372006-10-24 11:15:29 +0100227 if (req->cmd->handler == mconsole_go)
228 break;
229 if (req->cmd->handler == mconsole_stop) {
230 mconsole_reply(req, "Already stopped", 1, 0);
231 continue;
232 }
233 if (req->cmd->handler == mconsole_sysrq) {
234 struct pt_regs *old_regs;
235 old_regs = set_irq_regs((struct pt_regs *)&req->regs);
236 mconsole_sysrq(req);
237 set_irq_regs(old_regs);
238 continue;
239 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 (*req->cmd->handler)(req);
241 }
242 os_set_fd_block(req->originating_fd, 0);
243 reactivate_fd(req->originating_fd, MCONSOLE_IRQ);
244 mconsole_reply(req, "", 0, 0);
245}
246
Jeff Dike84f48d42007-02-10 01:44:01 -0800247static DEFINE_SPINLOCK(mc_devices_lock);
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800248static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
250void mconsole_register_dev(struct mc_device *new)
251{
Jeff Dike84f48d42007-02-10 01:44:01 -0800252 spin_lock(&mc_devices_lock);
253 BUG_ON(!list_empty(&new->list));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 list_add(&new->list, &mconsole_devices);
Jeff Dike84f48d42007-02-10 01:44:01 -0800255 spin_unlock(&mc_devices_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256}
257
258static struct mc_device *mconsole_find_dev(char *name)
259{
260 struct list_head *ele;
261 struct mc_device *dev;
262
Jeff Dikeae2587e2007-10-16 01:26:57 -0700263 list_for_each(ele, &mconsole_devices) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 dev = list_entry(ele, struct mc_device, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700265 if (!strncmp(name, dev->name, strlen(dev->name)))
266 return dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700268 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269}
270
Jeff Dike02dea082006-03-31 02:30:08 -0800271#define UNPLUGGED_PER_PAGE \
272 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
273
274struct unplugged_pages {
275 struct list_head list;
276 void *pages[UNPLUGGED_PER_PAGE];
277};
278
Daniel Walkere98fa282008-02-04 22:31:27 -0800279static DEFINE_MUTEX(plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800280static unsigned long long unplugged_pages_count = 0;
Jeff Dikec59bce62007-02-10 01:44:04 -0800281static LIST_HEAD(unplugged_pages);
Jeff Dike02dea082006-03-31 02:30:08 -0800282static int unplug_index = UNPLUGGED_PER_PAGE;
283
Jeff Dikef28169d2007-02-10 01:43:53 -0800284static int mem_config(char *str, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800285{
286 unsigned long long diff;
287 int err = -EINVAL, i, add;
288 char *ret;
289
Jeff Dikeae2587e2007-10-16 01:26:57 -0700290 if (str[0] != '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800291 *error_out = "Expected '=' after 'mem'";
Jeff Dike02dea082006-03-31 02:30:08 -0800292 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800293 }
Jeff Dike02dea082006-03-31 02:30:08 -0800294
295 str++;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700296 if (str[0] == '-')
Jeff Dike02dea082006-03-31 02:30:08 -0800297 add = 0;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700298 else if (str[0] == '+') {
Jeff Dike02dea082006-03-31 02:30:08 -0800299 add = 1;
300 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800301 else {
302 *error_out = "Expected increment to start with '-' or '+'";
303 goto out;
304 }
Jeff Dike02dea082006-03-31 02:30:08 -0800305
306 str++;
307 diff = memparse(str, &ret);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700308 if (*ret != '\0') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800309 *error_out = "Failed to parse memory increment";
Jeff Dike02dea082006-03-31 02:30:08 -0800310 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800311 }
Jeff Dike02dea082006-03-31 02:30:08 -0800312
313 diff /= PAGE_SIZE;
314
Daniel Walkere98fa282008-02-04 22:31:27 -0800315 mutex_lock(&plug_mem_mutex);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700316 for (i = 0; i < diff; i++) {
Jeff Dike02dea082006-03-31 02:30:08 -0800317 struct unplugged_pages *unplugged;
318 void *addr;
319
Jeff Dikeae2587e2007-10-16 01:26:57 -0700320 if (add) {
321 if (list_empty(&unplugged_pages))
Jeff Dike02dea082006-03-31 02:30:08 -0800322 break;
323
324 unplugged = list_entry(unplugged_pages.next,
325 struct unplugged_pages, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700326 if (unplug_index > 0)
Jeff Dike02dea082006-03-31 02:30:08 -0800327 addr = unplugged->pages[--unplug_index];
328 else {
329 list_del(&unplugged->list);
330 addr = unplugged;
331 unplug_index = UNPLUGGED_PER_PAGE;
332 }
333
334 free_page((unsigned long) addr);
335 unplugged_pages_count--;
336 }
337 else {
338 struct page *page;
339
340 page = alloc_page(GFP_ATOMIC);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700341 if (page == NULL)
Jeff Dike02dea082006-03-31 02:30:08 -0800342 break;
343
344 unplugged = page_address(page);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700345 if (unplug_index == UNPLUGGED_PER_PAGE) {
Jeff Dike02dea082006-03-31 02:30:08 -0800346 list_add(&unplugged->list, &unplugged_pages);
347 unplug_index = 0;
348 }
349 else {
350 struct list_head *entry = unplugged_pages.next;
351 addr = unplugged;
352
353 unplugged = list_entry(entry,
354 struct unplugged_pages,
355 list);
Jeff Dike02dea082006-03-31 02:30:08 -0800356 err = os_drop_memory(addr, PAGE_SIZE);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700357 if (err) {
358 printk(KERN_ERR "Failed to release "
359 "memory - errno = %d\n", err);
Jeff Dikef28169d2007-02-10 01:43:53 -0800360 *error_out = "Failed to release memory";
Jeff Dike84f48d42007-02-10 01:44:01 -0800361 goto out_unlock;
Jeff Dikef28169d2007-02-10 01:43:53 -0800362 }
363 unplugged->pages[unplug_index++] = addr;
Jeff Dike02dea082006-03-31 02:30:08 -0800364 }
365
366 unplugged_pages_count++;
367 }
368 }
369
370 err = 0;
Jeff Dike84f48d42007-02-10 01:44:01 -0800371out_unlock:
Daniel Walkere98fa282008-02-04 22:31:27 -0800372 mutex_unlock(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800373out:
374 return err;
375}
376
377static int mem_get_config(char *name, char *str, int size, char **error_out)
378{
379 char buf[sizeof("18446744073709551615")];
380 int len = 0;
381
382 sprintf(buf, "%ld", uml_physmem);
383 CONFIG_CHUNK(str, size, len, buf, 1);
384
385 return len;
386}
387
388static int mem_id(char **str, int *start_out, int *end_out)
389{
390 *start_out = 0;
391 *end_out = 0;
392
393 return 0;
394}
395
Jeff Dikef28169d2007-02-10 01:43:53 -0800396static int mem_remove(int n, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800397{
Jeff Dikef28169d2007-02-10 01:43:53 -0800398 *error_out = "Memory doesn't support the remove operation";
Jeff Dike02dea082006-03-31 02:30:08 -0800399 return -EBUSY;
400}
401
402static struct mc_device mem_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800403 .list = LIST_HEAD_INIT(mem_mc.list),
Jeff Dike02dea082006-03-31 02:30:08 -0800404 .name = "mem",
405 .config = mem_config,
406 .get_config = mem_get_config,
407 .id = mem_id,
408 .remove = mem_remove,
409};
410
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700411static int __init mem_mc_init(void)
Jeff Dike02dea082006-03-31 02:30:08 -0800412{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700413 if (can_drop_memory())
Jeff Dike02dea082006-03-31 02:30:08 -0800414 mconsole_register_dev(&mem_mc);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700415 else printk(KERN_ERR "Can't release memory to the host - memory "
416 "hotplug won't be supported\n");
Jeff Dike02dea082006-03-31 02:30:08 -0800417 return 0;
418}
419
420__initcall(mem_mc_init);
421
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422#define CONFIG_BUF_SIZE 64
423
Jeff Diked50084a2006-01-06 00:18:50 -0800424static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 char **),
426 struct mc_request *req, char *name)
427{
428 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
429 int n, size;
430
Jeff Dikeae2587e2007-10-16 01:26:57 -0700431 if (get_config == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 mconsole_reply(req, "No get_config routine defined", 1, 0);
433 return;
434 }
435
436 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700437 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 buf = default_buf;
439
Jeff Dikeae2587e2007-10-16 01:26:57 -0700440 while (1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 n = (*get_config)(name, buf, size, &error);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700442 if (error != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 mconsole_reply(req, error, 1, 0);
444 goto out;
445 }
446
Jeff Dikeae2587e2007-10-16 01:26:57 -0700447 if (n <= size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 mconsole_reply(req, buf, 0, 0);
449 goto out;
450 }
451
Jeff Dikeae2587e2007-10-16 01:26:57 -0700452 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 kfree(buf);
454
455 size = n;
456 buf = kmalloc(size, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700457 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
459 return;
460 }
461 }
462 out:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700463 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465}
466
467void mconsole_config(struct mc_request *req)
468{
469 struct mc_device *dev;
Jeff Dikef28169d2007-02-10 01:43:53 -0800470 char *ptr = req->request.data, *name, *error_string = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 int err;
472
473 ptr += strlen("config");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800474 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700476 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 mconsole_reply(req, "Bad configuration option", 1, 0);
478 return;
479 }
480
481 name = &ptr[strlen(dev->name)];
482 ptr = name;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700483 while ((*ptr != '=') && (*ptr != '\0'))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 ptr++;
485
Jeff Dikeae2587e2007-10-16 01:26:57 -0700486 if (*ptr == '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800487 err = (*dev->config)(name, &error_string);
488 mconsole_reply(req, error_string, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 }
490 else mconsole_get_config(dev->get_config, req, name);
491}
492
493void mconsole_remove(struct mc_request *req)
494{
Jeff Diked50084a2006-01-06 00:18:50 -0800495 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700496 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800497 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700498 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499
500 ptr += strlen("remove");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800501 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700503 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 mconsole_reply(req, "Bad remove option", 1, 0);
505 return;
506 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700507
Jeff Dike3a331a52006-01-06 00:19:05 -0800508 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700509
Jeff Dike3a331a52006-01-06 00:19:05 -0800510 err = 1;
511 n = (*dev->id)(&ptr, &start, &end);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700512 if (n < 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800513 err_msg = "Couldn't parse device number";
514 goto out;
515 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700516 else if ((n < start) || (n > end)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800517 sprintf(error, "Invalid device number - must be between "
518 "%d and %d", start, end);
519 err_msg = error;
520 goto out;
521 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700522
Jeff Dikef28169d2007-02-10 01:43:53 -0800523 err_msg = NULL;
524 err = (*dev->remove)(n, &err_msg);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700525 switch(err) {
Jeff Diked40f6d72007-03-29 01:20:28 -0700526 case 0:
527 err_msg = "";
528 break;
Jeff Dike3a331a52006-01-06 00:19:05 -0800529 case -ENODEV:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700530 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800531 err_msg = "Device doesn't exist";
Jeff Dike3a331a52006-01-06 00:19:05 -0800532 break;
533 case -EBUSY:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700534 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800535 err_msg = "Device is currently open";
Jeff Dike3a331a52006-01-06 00:19:05 -0800536 break;
537 default:
538 break;
539 }
540out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700541 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542}
543
Jeff Dikef92afe52006-09-29 01:58:52 -0700544struct mconsole_output {
545 struct list_head list;
546 struct mc_request *req;
547};
548
Jeff Dike84f48d42007-02-10 01:44:01 -0800549static DEFINE_SPINLOCK(client_lock);
Jeff Dike6f517d32006-01-06 00:19:04 -0800550static LIST_HEAD(clients);
551static char console_buf[MCONSOLE_MAX_DATA];
Jeff Dike6f517d32006-01-06 00:19:04 -0800552
553static void console_write(struct console *console, const char *string,
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700554 unsigned int len)
Jeff Dike6f517d32006-01-06 00:19:04 -0800555{
556 struct list_head *ele;
557 int n;
558
Jeff Dikeae2587e2007-10-16 01:26:57 -0700559 if (list_empty(&clients))
Jeff Dike6f517d32006-01-06 00:19:04 -0800560 return;
561
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700562 while (len > 0) {
563 n = min((size_t) len, ARRAY_SIZE(console_buf));
564 strncpy(console_buf, string, n);
Jeff Dike6f517d32006-01-06 00:19:04 -0800565 string += n;
566 len -= n;
Jeff Dike6f517d32006-01-06 00:19:04 -0800567
Jeff Dikeae2587e2007-10-16 01:26:57 -0700568 list_for_each(ele, &clients) {
Jeff Dikef92afe52006-09-29 01:58:52 -0700569 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800570
Jeff Dikef92afe52006-09-29 01:58:52 -0700571 entry = list_entry(ele, struct mconsole_output, list);
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700572 mconsole_reply_len(entry->req, console_buf, n, 0, 1);
Jeff Dike6f517d32006-01-06 00:19:04 -0800573 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800574 }
575}
576
577static struct console mc_console = { .name = "mc",
578 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800579 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800580 .index = -1 };
581
582static int mc_add_console(void)
583{
584 register_console(&mc_console);
585 return 0;
586}
587
588late_initcall(mc_add_console);
589
590static void with_console(struct mc_request *req, void (*proc)(void *),
591 void *arg)
592{
Jeff Dikef92afe52006-09-29 01:58:52 -0700593 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800594 unsigned long flags;
595
Jeff Dikef92afe52006-09-29 01:58:52 -0700596 entry.req = req;
Jeff Dike84f48d42007-02-10 01:44:01 -0800597 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800598 list_add(&entry.list, &clients);
Jeff Dike84f48d42007-02-10 01:44:01 -0800599 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800600
601 (*proc)(arg);
602
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700603 mconsole_reply_len(req, "", 0, 0, 0);
Jeff Dike6f517d32006-01-06 00:19:04 -0800604
Jeff Dike84f48d42007-02-10 01:44:01 -0800605 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800606 list_del(&entry.list);
Jeff Dike84f48d42007-02-10 01:44:01 -0800607 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800608}
609
Jeff Dike4111b022006-01-06 00:19:05 -0800610#ifdef CONFIG_MAGIC_SYSRQ
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700611
612#include <linux/sysrq.h>
613
Jeff Dike4111b022006-01-06 00:19:05 -0800614static void sysrq_proc(void *arg)
615{
616 char *op = arg;
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700617 handle_sysrq(*op);
Jeff Dike4111b022006-01-06 00:19:05 -0800618}
619
620void mconsole_sysrq(struct mc_request *req)
621{
622 char *ptr = req->request.data;
623
624 ptr += strlen("sysrq");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800625 ptr = skip_spaces(ptr);
Jeff Dike4111b022006-01-06 00:19:05 -0800626
Jeff Dikeae2587e2007-10-16 01:26:57 -0700627 /*
628 * With 'b', the system will shut down without a chance to reply,
Jeff Dike4111b022006-01-06 00:19:05 -0800629 * so in this case, we reply first.
630 */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700631 if (*ptr == 'b')
Jeff Dike4111b022006-01-06 00:19:05 -0800632 mconsole_reply(req, "", 0, 0);
633
634 with_console(req, sysrq_proc, ptr);
635}
636#else
637void mconsole_sysrq(struct mc_request *req)
638{
639 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
640}
641#endif
642
Jeff Dike6f517d32006-01-06 00:19:04 -0800643static void stack_proc(void *arg)
644{
Richard Weinbergera1850e92013-09-23 17:38:03 +0200645 struct task_struct *task = arg;
Jeff Dike6f517d32006-01-06 00:19:04 -0800646
Richard Weinbergera1850e92013-09-23 17:38:03 +0200647 show_stack(task, NULL);
Jeff Dike6f517d32006-01-06 00:19:04 -0800648}
649
Jeff Dikeae2587e2007-10-16 01:26:57 -0700650/*
651 * Mconsole stack trace
Jeff Dike3eddddc2005-09-16 19:27:46 -0700652 * Added by Allan Graves, Jeff Dike
653 * Dumps a stacks registers to the linux console.
654 * Usage stack <pid>.
655 */
Jeff Dike42fda662007-10-16 01:26:50 -0700656void mconsole_stack(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700657{
Jeff Dike3a331a52006-01-06 00:19:05 -0800658 char *ptr = req->request.data;
659 int pid_requested= -1;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700660 struct task_struct *to = NULL;
661
Jeff Dikeae2587e2007-10-16 01:26:57 -0700662 /*
663 * Would be nice:
Jeff Dike3a331a52006-01-06 00:19:05 -0800664 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700665 * 2) Add a way to stack dump all pids.
666 */
667
Jeff Dike3a331a52006-01-06 00:19:05 -0800668 ptr += strlen("stack");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800669 ptr = skip_spaces(ptr);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700670
Jeff Dikeae2587e2007-10-16 01:26:57 -0700671 /*
672 * Should really check for multiple pids or reject bad args here
673 */
Jeff Dike3a331a52006-01-06 00:19:05 -0800674 /* What do the arguments in mconsole_reply mean? */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700675 if (sscanf(ptr, "%d", &pid_requested) == 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800676 mconsole_reply(req, "Please specify a pid", 1, 0);
677 return;
678 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700679
Jeff Dike827b3f62008-02-04 22:31:29 -0800680 to = find_task_by_pid_ns(pid_requested, &init_pid_ns);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700681 if ((to == NULL) || (pid_requested == 0)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800682 mconsole_reply(req, "Couldn't find that pid", 1, 0);
683 return;
684 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800685 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700686}
Jeff Dike3eddddc2005-09-16 19:27:46 -0700687
Jeff Dikeae2587e2007-10-16 01:26:57 -0700688/*
689 * Changed by mconsole_setup, which is __setup, and called before SMP is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 * active.
691 */
Jeff Diked50084a2006-01-06 00:18:50 -0800692static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700694static int __init mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695{
696 /* long to avoid size mismatch warnings from gcc */
697 long sock;
698 int err;
Balbir Singh36137122008-12-09 13:14:07 -0800699 char file[UNIX_PATH_MAX];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700
Jeff Dikeae2587e2007-10-16 01:26:57 -0700701 if (umid_file_name("mconsole", file, sizeof(file)))
702 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
704
705 sock = os_create_unix_socket(file, sizeof(file), 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700706 if (sock < 0) {
707 printk(KERN_ERR "Failed to initialize management console\n");
708 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 }
Jeff Dike438ee672008-02-04 22:31:19 -0800710 if (os_set_fd_block(sock, 0))
711 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712
713 register_reboot_notifier(&reboot_notifier);
714
715 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Theodore Ts'oaab94462012-07-17 14:18:23 -0400716 IRQF_SHARED, "mconsole", (void *)sock);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700717 if (err) {
718 printk(KERN_ERR "Failed to get IRQ for management console\n");
Jeff Dike438ee672008-02-04 22:31:19 -0800719 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 }
721
Jeff Dikeae2587e2007-10-16 01:26:57 -0700722 if (notify_socket != NULL) {
Jeff Dike970d6e32006-01-06 00:18:48 -0800723 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700724 if (notify_socket != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800726 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 strlen(mconsole_socket_name) + 1);
728 else printk(KERN_ERR "mconsole_setup failed to strdup "
729 "string\n");
730 }
731
Jeff Dikeae2587e2007-10-16 01:26:57 -0700732 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 MCONSOLE_VERSION, mconsole_socket_name);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700734 return 0;
Jeff Dike438ee672008-02-04 22:31:19 -0800735
736 out:
737 os_close_file(sock);
738 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739}
740
741__initcall(mconsole_init);
742
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800743static ssize_t mconsole_proc_write(struct file *file,
744 const char __user *buffer, size_t count, loff_t *pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745{
746 char *buf;
747
Al Viro793b7962016-01-02 14:53:28 -0500748 buf = memdup_user_nul(buffer, count);
749 if (IS_ERR(buf))
750 return PTR_ERR(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751
752 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 kfree(buf);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700754 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755}
756
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800757static const struct file_operations mconsole_proc_fops = {
758 .owner = THIS_MODULE,
759 .write = mconsole_proc_write,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200760 .llseek = noop_llseek,
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800761};
762
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763static int create_proc_mconsole(void)
764{
765 struct proc_dir_entry *ent;
766
Jeff Dikeae2587e2007-10-16 01:26:57 -0700767 if (notify_socket == NULL)
768 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800770 ent = proc_create("mconsole", 0200, NULL, &mconsole_proc_fops);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700771 if (ent == NULL) {
David Howells4554eb92013-04-04 16:58:25 +0100772 printk(KERN_INFO "create_proc_mconsole : proc_create failed\n");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700773 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700775 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776}
777
778static DEFINE_SPINLOCK(notify_spinlock);
779
780void lock_notify(void)
781{
782 spin_lock(&notify_spinlock);
783}
784
785void unlock_notify(void)
786{
787 spin_unlock(&notify_spinlock);
788}
789
790__initcall(create_proc_mconsole);
791
Jeff Dike088bec42007-10-16 01:27:20 -0700792#define NOTIFY "notify:"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
794static int mconsole_setup(char *str)
795{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700796 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 str += strlen(NOTIFY);
798 notify_socket = str;
799 }
800 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700801 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802}
803
Jeff Dike088bec42007-10-16 01:27:20 -0700804__setup("mconsole=", mconsole_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
806__uml_help(mconsole_setup,
807"mconsole=notify:<socket>\n"
808" Requests that the mconsole driver send a message to the named Unix\n"
809" socket containing the name of the mconsole socket. This also serves\n"
810" to notify outside processes when UML has booted far enough to respond\n"
811" to mconsole requests.\n\n"
812);
813
814static int notify_panic(struct notifier_block *self, unsigned long unused1,
815 void *ptr)
816{
817 char *message = ptr;
818
Jeff Dikeae2587e2007-10-16 01:26:57 -0700819 if (notify_socket == NULL)
820 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821
Jeff Diked50084a2006-01-06 00:18:50 -0800822 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 strlen(message) + 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700824 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825}
826
827static struct notifier_block panic_exit_notifier = {
828 .notifier_call = notify_panic,
829 .next = NULL,
830 .priority = 1
831};
832
833static int add_notifier(void)
834{
Alan Sterne041c682006-03-27 01:16:30 -0800835 atomic_notifier_chain_register(&panic_notifier_list,
836 &panic_exit_notifier);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700837 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838}
839
840__initcall(add_notifier);
841
842char *mconsole_notify_socket(void)
843{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700844 return notify_socket;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845}
846
847EXPORT_SYMBOL(mconsole_notify_socket);