blob: b80a1d616e4ea5b7a21151e29ea5c740ba3faac1 [file] [log] [blame]
Alex Dewardbddf422019-08-25 10:49:16 +01001// SPDX-License-Identifier: GPL-2.0
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
Jeff Dike827b3f62008-02-04 22:31:29 -08004 * Copyright (C) 2001 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 */
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);
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;
Al Viro7a5016092018-02-10 01:35:16 +0000132 loff_t pos = 0;
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 {
Al Viro7a5016092018-02-10 01:35:16 +0000151 len = kernel_read(file, buf, PAGE_SIZE - 1, &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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 mconsole_reply(req, "", 0, 0);
243}
244
Jeff Dike84f48d42007-02-10 01:44:01 -0800245static DEFINE_SPINLOCK(mc_devices_lock);
Paolo 'Blaisorblade' Giarrusso42947cb2006-02-01 03:06:29 -0800246static LIST_HEAD(mconsole_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247
248void mconsole_register_dev(struct mc_device *new)
249{
Jeff Dike84f48d42007-02-10 01:44:01 -0800250 spin_lock(&mc_devices_lock);
251 BUG_ON(!list_empty(&new->list));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 list_add(&new->list, &mconsole_devices);
Jeff Dike84f48d42007-02-10 01:44:01 -0800253 spin_unlock(&mc_devices_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254}
255
256static struct mc_device *mconsole_find_dev(char *name)
257{
258 struct list_head *ele;
259 struct mc_device *dev;
260
Jeff Dikeae2587e2007-10-16 01:26:57 -0700261 list_for_each(ele, &mconsole_devices) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 dev = list_entry(ele, struct mc_device, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700263 if (!strncmp(name, dev->name, strlen(dev->name)))
264 return dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700266 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267}
268
Jeff Dike02dea082006-03-31 02:30:08 -0800269#define UNPLUGGED_PER_PAGE \
270 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
271
272struct unplugged_pages {
273 struct list_head list;
274 void *pages[UNPLUGGED_PER_PAGE];
275};
276
Daniel Walkere98fa282008-02-04 22:31:27 -0800277static DEFINE_MUTEX(plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800278static unsigned long long unplugged_pages_count = 0;
Jeff Dikec59bce62007-02-10 01:44:04 -0800279static LIST_HEAD(unplugged_pages);
Jeff Dike02dea082006-03-31 02:30:08 -0800280static int unplug_index = UNPLUGGED_PER_PAGE;
281
Jeff Dikef28169d2007-02-10 01:43:53 -0800282static int mem_config(char *str, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800283{
284 unsigned long long diff;
285 int err = -EINVAL, i, add;
286 char *ret;
287
Jeff Dikeae2587e2007-10-16 01:26:57 -0700288 if (str[0] != '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800289 *error_out = "Expected '=' after 'mem'";
Jeff Dike02dea082006-03-31 02:30:08 -0800290 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800291 }
Jeff Dike02dea082006-03-31 02:30:08 -0800292
293 str++;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700294 if (str[0] == '-')
Jeff Dike02dea082006-03-31 02:30:08 -0800295 add = 0;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700296 else if (str[0] == '+') {
Jeff Dike02dea082006-03-31 02:30:08 -0800297 add = 1;
298 }
Jeff Dikef28169d2007-02-10 01:43:53 -0800299 else {
300 *error_out = "Expected increment to start with '-' or '+'";
301 goto out;
302 }
Jeff Dike02dea082006-03-31 02:30:08 -0800303
304 str++;
305 diff = memparse(str, &ret);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700306 if (*ret != '\0') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800307 *error_out = "Failed to parse memory increment";
Jeff Dike02dea082006-03-31 02:30:08 -0800308 goto out;
Jeff Dikef28169d2007-02-10 01:43:53 -0800309 }
Jeff Dike02dea082006-03-31 02:30:08 -0800310
311 diff /= PAGE_SIZE;
312
Daniel Walkere98fa282008-02-04 22:31:27 -0800313 mutex_lock(&plug_mem_mutex);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700314 for (i = 0; i < diff; i++) {
Jeff Dike02dea082006-03-31 02:30:08 -0800315 struct unplugged_pages *unplugged;
316 void *addr;
317
Jeff Dikeae2587e2007-10-16 01:26:57 -0700318 if (add) {
319 if (list_empty(&unplugged_pages))
Jeff Dike02dea082006-03-31 02:30:08 -0800320 break;
321
322 unplugged = list_entry(unplugged_pages.next,
323 struct unplugged_pages, list);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700324 if (unplug_index > 0)
Jeff Dike02dea082006-03-31 02:30:08 -0800325 addr = unplugged->pages[--unplug_index];
326 else {
327 list_del(&unplugged->list);
328 addr = unplugged;
329 unplug_index = UNPLUGGED_PER_PAGE;
330 }
331
332 free_page((unsigned long) addr);
333 unplugged_pages_count--;
334 }
335 else {
336 struct page *page;
337
338 page = alloc_page(GFP_ATOMIC);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700339 if (page == NULL)
Jeff Dike02dea082006-03-31 02:30:08 -0800340 break;
341
342 unplugged = page_address(page);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700343 if (unplug_index == UNPLUGGED_PER_PAGE) {
Jeff Dike02dea082006-03-31 02:30:08 -0800344 list_add(&unplugged->list, &unplugged_pages);
345 unplug_index = 0;
346 }
347 else {
348 struct list_head *entry = unplugged_pages.next;
349 addr = unplugged;
350
351 unplugged = list_entry(entry,
352 struct unplugged_pages,
353 list);
Jeff Dike02dea082006-03-31 02:30:08 -0800354 err = os_drop_memory(addr, PAGE_SIZE);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700355 if (err) {
356 printk(KERN_ERR "Failed to release "
357 "memory - errno = %d\n", err);
Jeff Dikef28169d2007-02-10 01:43:53 -0800358 *error_out = "Failed to release memory";
Jeff Dike84f48d42007-02-10 01:44:01 -0800359 goto out_unlock;
Jeff Dikef28169d2007-02-10 01:43:53 -0800360 }
361 unplugged->pages[unplug_index++] = addr;
Jeff Dike02dea082006-03-31 02:30:08 -0800362 }
363
364 unplugged_pages_count++;
365 }
366 }
367
368 err = 0;
Jeff Dike84f48d42007-02-10 01:44:01 -0800369out_unlock:
Daniel Walkere98fa282008-02-04 22:31:27 -0800370 mutex_unlock(&plug_mem_mutex);
Jeff Dike02dea082006-03-31 02:30:08 -0800371out:
372 return err;
373}
374
375static int mem_get_config(char *name, char *str, int size, char **error_out)
376{
377 char buf[sizeof("18446744073709551615")];
378 int len = 0;
379
380 sprintf(buf, "%ld", uml_physmem);
381 CONFIG_CHUNK(str, size, len, buf, 1);
382
383 return len;
384}
385
386static int mem_id(char **str, int *start_out, int *end_out)
387{
388 *start_out = 0;
389 *end_out = 0;
390
391 return 0;
392}
393
Jeff Dikef28169d2007-02-10 01:43:53 -0800394static int mem_remove(int n, char **error_out)
Jeff Dike02dea082006-03-31 02:30:08 -0800395{
Jeff Dikef28169d2007-02-10 01:43:53 -0800396 *error_out = "Memory doesn't support the remove operation";
Jeff Dike02dea082006-03-31 02:30:08 -0800397 return -EBUSY;
398}
399
400static struct mc_device mem_mc = {
Jeff Dike84f48d42007-02-10 01:44:01 -0800401 .list = LIST_HEAD_INIT(mem_mc.list),
Jeff Dike02dea082006-03-31 02:30:08 -0800402 .name = "mem",
403 .config = mem_config,
404 .get_config = mem_get_config,
405 .id = mem_id,
406 .remove = mem_remove,
407};
408
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700409static int __init mem_mc_init(void)
Jeff Dike02dea082006-03-31 02:30:08 -0800410{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700411 if (can_drop_memory())
Jeff Dike02dea082006-03-31 02:30:08 -0800412 mconsole_register_dev(&mem_mc);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700413 else printk(KERN_ERR "Can't release memory to the host - memory "
414 "hotplug won't be supported\n");
Jeff Dike02dea082006-03-31 02:30:08 -0800415 return 0;
416}
417
418__initcall(mem_mc_init);
419
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420#define CONFIG_BUF_SIZE 64
421
Jeff Diked50084a2006-01-06 00:18:50 -0800422static void mconsole_get_config(int (*get_config)(char *, char *, int,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 char **),
424 struct mc_request *req, char *name)
425{
426 char default_buf[CONFIG_BUF_SIZE], *error, *buf;
427 int n, size;
428
Jeff Dikeae2587e2007-10-16 01:26:57 -0700429 if (get_config == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 mconsole_reply(req, "No get_config routine defined", 1, 0);
431 return;
432 }
433
434 error = NULL;
Jeff Dike91b165c2006-09-25 23:33:00 -0700435 size = ARRAY_SIZE(default_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 buf = default_buf;
437
Jeff Dikeae2587e2007-10-16 01:26:57 -0700438 while (1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 n = (*get_config)(name, buf, size, &error);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700440 if (error != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 mconsole_reply(req, error, 1, 0);
442 goto out;
443 }
444
Jeff Dikeae2587e2007-10-16 01:26:57 -0700445 if (n <= size) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 mconsole_reply(req, buf, 0, 0);
447 goto out;
448 }
449
Jeff Dikeae2587e2007-10-16 01:26:57 -0700450 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451 kfree(buf);
452
453 size = n;
454 buf = kmalloc(size, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700455 if (buf == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 mconsole_reply(req, "Failed to allocate buffer", 1, 0);
457 return;
458 }
459 }
460 out:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700461 if (buf != default_buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 kfree(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463}
464
465void mconsole_config(struct mc_request *req)
466{
467 struct mc_device *dev;
Jeff Dikef28169d2007-02-10 01:43:53 -0800468 char *ptr = req->request.data, *name, *error_string = "";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 int err;
470
471 ptr += strlen("config");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800472 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700474 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 mconsole_reply(req, "Bad configuration option", 1, 0);
476 return;
477 }
478
479 name = &ptr[strlen(dev->name)];
480 ptr = name;
Jeff Dikeae2587e2007-10-16 01:26:57 -0700481 while ((*ptr != '=') && (*ptr != '\0'))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 ptr++;
483
Jeff Dikeae2587e2007-10-16 01:26:57 -0700484 if (*ptr == '=') {
Jeff Dikef28169d2007-02-10 01:43:53 -0800485 err = (*dev->config)(name, &error_string);
486 mconsole_reply(req, error_string, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 }
488 else mconsole_get_config(dev->get_config, req, name);
489}
490
491void mconsole_remove(struct mc_request *req)
492{
Jeff Diked50084a2006-01-06 00:18:50 -0800493 struct mc_device *dev;
Jeff Dike29d56cf2005-06-25 14:55:25 -0700494 char *ptr = req->request.data, *err_msg = "";
Jeff Dike3a331a52006-01-06 00:19:05 -0800495 char error[256];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700496 int err, start, end, n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
498 ptr += strlen("remove");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800499 ptr = skip_spaces(ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 dev = mconsole_find_dev(ptr);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700501 if (dev == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 mconsole_reply(req, "Bad remove option", 1, 0);
503 return;
504 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700505
Jeff Dike3a331a52006-01-06 00:19:05 -0800506 ptr = &ptr[strlen(dev->name)];
Jeff Dike29d56cf2005-06-25 14:55:25 -0700507
Jeff Dike3a331a52006-01-06 00:19:05 -0800508 err = 1;
509 n = (*dev->id)(&ptr, &start, &end);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700510 if (n < 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800511 err_msg = "Couldn't parse device number";
512 goto out;
513 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700514 else if ((n < start) || (n > end)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800515 sprintf(error, "Invalid device number - must be between "
516 "%d and %d", start, end);
517 err_msg = error;
518 goto out;
519 }
Jeff Dike29d56cf2005-06-25 14:55:25 -0700520
Jeff Dikef28169d2007-02-10 01:43:53 -0800521 err_msg = NULL;
522 err = (*dev->remove)(n, &err_msg);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700523 switch(err) {
Jeff Diked40f6d72007-03-29 01:20:28 -0700524 case 0:
525 err_msg = "";
526 break;
Jeff Dike3a331a52006-01-06 00:19:05 -0800527 case -ENODEV:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700528 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800529 err_msg = "Device doesn't exist";
Jeff Dike3a331a52006-01-06 00:19:05 -0800530 break;
531 case -EBUSY:
Jeff Dikeae2587e2007-10-16 01:26:57 -0700532 if (err_msg == NULL)
Jeff Dikef28169d2007-02-10 01:43:53 -0800533 err_msg = "Device is currently open";
Jeff Dike3a331a52006-01-06 00:19:05 -0800534 break;
535 default:
536 break;
537 }
538out:
Jeff Dike29d56cf2005-06-25 14:55:25 -0700539 mconsole_reply(req, err_msg, err, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540}
541
Jeff Dikef92afe52006-09-29 01:58:52 -0700542struct mconsole_output {
543 struct list_head list;
544 struct mc_request *req;
545};
546
Jeff Dike84f48d42007-02-10 01:44:01 -0800547static DEFINE_SPINLOCK(client_lock);
Jeff Dike6f517d32006-01-06 00:19:04 -0800548static LIST_HEAD(clients);
549static char console_buf[MCONSOLE_MAX_DATA];
Jeff Dike6f517d32006-01-06 00:19:04 -0800550
551static void console_write(struct console *console, const char *string,
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700552 unsigned int len)
Jeff Dike6f517d32006-01-06 00:19:04 -0800553{
554 struct list_head *ele;
555 int n;
556
Jeff Dikeae2587e2007-10-16 01:26:57 -0700557 if (list_empty(&clients))
Jeff Dike6f517d32006-01-06 00:19:04 -0800558 return;
559
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700560 while (len > 0) {
561 n = min((size_t) len, ARRAY_SIZE(console_buf));
562 strncpy(console_buf, string, n);
Jeff Dike6f517d32006-01-06 00:19:04 -0800563 string += n;
564 len -= n;
Jeff Dike6f517d32006-01-06 00:19:04 -0800565
Jeff Dikeae2587e2007-10-16 01:26:57 -0700566 list_for_each(ele, &clients) {
Jeff Dikef92afe52006-09-29 01:58:52 -0700567 struct mconsole_output *entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800568
Jeff Dikef92afe52006-09-29 01:58:52 -0700569 entry = list_entry(ele, struct mconsole_output, list);
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700570 mconsole_reply_len(entry->req, console_buf, n, 0, 1);
Jeff Dike6f517d32006-01-06 00:19:04 -0800571 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800572 }
573}
574
575static struct console mc_console = { .name = "mc",
576 .write = console_write,
Jeff Dikea174b302006-01-11 12:17:28 -0800577 .flags = CON_ENABLED,
Jeff Dike6f517d32006-01-06 00:19:04 -0800578 .index = -1 };
579
580static int mc_add_console(void)
581{
582 register_console(&mc_console);
583 return 0;
584}
585
586late_initcall(mc_add_console);
587
588static void with_console(struct mc_request *req, void (*proc)(void *),
589 void *arg)
590{
Jeff Dikef92afe52006-09-29 01:58:52 -0700591 struct mconsole_output entry;
Jeff Dike6f517d32006-01-06 00:19:04 -0800592 unsigned long flags;
593
Jeff Dikef92afe52006-09-29 01:58:52 -0700594 entry.req = req;
Jeff Dike84f48d42007-02-10 01:44:01 -0800595 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800596 list_add(&entry.list, &clients);
Jeff Dike84f48d42007-02-10 01:44:01 -0800597 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800598
599 (*proc)(arg);
600
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700601 mconsole_reply_len(req, "", 0, 0, 0);
Jeff Dike6f517d32006-01-06 00:19:04 -0800602
Jeff Dike84f48d42007-02-10 01:44:01 -0800603 spin_lock_irqsave(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800604 list_del(&entry.list);
Jeff Dike84f48d42007-02-10 01:44:01 -0800605 spin_unlock_irqrestore(&client_lock, flags);
Jeff Dike6f517d32006-01-06 00:19:04 -0800606}
607
Jeff Dike4111b022006-01-06 00:19:05 -0800608#ifdef CONFIG_MAGIC_SYSRQ
Jeff Dike54fa0ba2007-10-16 01:27:17 -0700609
610#include <linux/sysrq.h>
611
Jeff Dike4111b022006-01-06 00:19:05 -0800612static void sysrq_proc(void *arg)
613{
614 char *op = arg;
Dmitry Torokhovf3353972010-08-17 21:15:47 -0700615 handle_sysrq(*op);
Jeff Dike4111b022006-01-06 00:19:05 -0800616}
617
618void mconsole_sysrq(struct mc_request *req)
619{
620 char *ptr = req->request.data;
621
622 ptr += strlen("sysrq");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800623 ptr = skip_spaces(ptr);
Jeff Dike4111b022006-01-06 00:19:05 -0800624
Jeff Dikeae2587e2007-10-16 01:26:57 -0700625 /*
626 * With 'b', the system will shut down without a chance to reply,
Jeff Dike4111b022006-01-06 00:19:05 -0800627 * so in this case, we reply first.
628 */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700629 if (*ptr == 'b')
Jeff Dike4111b022006-01-06 00:19:05 -0800630 mconsole_reply(req, "", 0, 0);
631
632 with_console(req, sysrq_proc, ptr);
633}
634#else
635void mconsole_sysrq(struct mc_request *req)
636{
637 mconsole_reply(req, "Sysrq not compiled in", 1, 0);
638}
639#endif
640
Jeff Dike6f517d32006-01-06 00:19:04 -0800641static void stack_proc(void *arg)
642{
Richard Weinbergera1850e92013-09-23 17:38:03 +0200643 struct task_struct *task = arg;
Jeff Dike6f517d32006-01-06 00:19:04 -0800644
Richard Weinbergera1850e92013-09-23 17:38:03 +0200645 show_stack(task, NULL);
Jeff Dike6f517d32006-01-06 00:19:04 -0800646}
647
Jeff Dikeae2587e2007-10-16 01:26:57 -0700648/*
649 * Mconsole stack trace
Jeff Dike3eddddc2005-09-16 19:27:46 -0700650 * Added by Allan Graves, Jeff Dike
651 * Dumps a stacks registers to the linux console.
652 * Usage stack <pid>.
653 */
Jeff Dike42fda662007-10-16 01:26:50 -0700654void mconsole_stack(struct mc_request *req)
Jeff Dike3eddddc2005-09-16 19:27:46 -0700655{
Jeff Dike3a331a52006-01-06 00:19:05 -0800656 char *ptr = req->request.data;
657 int pid_requested= -1;
Jeff Dike3eddddc2005-09-16 19:27:46 -0700658 struct task_struct *to = NULL;
659
Jeff Dikeae2587e2007-10-16 01:26:57 -0700660 /*
661 * Would be nice:
Jeff Dike3a331a52006-01-06 00:19:05 -0800662 * 1) Send showregs output to mconsole.
Jeff Dike3eddddc2005-09-16 19:27:46 -0700663 * 2) Add a way to stack dump all pids.
664 */
665
Jeff Dike3a331a52006-01-06 00:19:05 -0800666 ptr += strlen("stack");
André Goddard Rosae7d28602009-12-14 18:01:06 -0800667 ptr = skip_spaces(ptr);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700668
Jeff Dikeae2587e2007-10-16 01:26:57 -0700669 /*
670 * Should really check for multiple pids or reject bad args here
671 */
Jeff Dike3a331a52006-01-06 00:19:05 -0800672 /* What do the arguments in mconsole_reply mean? */
Jeff Dikeae2587e2007-10-16 01:26:57 -0700673 if (sscanf(ptr, "%d", &pid_requested) == 0) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800674 mconsole_reply(req, "Please specify a pid", 1, 0);
675 return;
676 }
Jeff Dike3eddddc2005-09-16 19:27:46 -0700677
Jeff Dike827b3f62008-02-04 22:31:29 -0800678 to = find_task_by_pid_ns(pid_requested, &init_pid_ns);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700679 if ((to == NULL) || (pid_requested == 0)) {
Jeff Dike3a331a52006-01-06 00:19:05 -0800680 mconsole_reply(req, "Couldn't find that pid", 1, 0);
681 return;
682 }
Jeff Dike6f517d32006-01-06 00:19:04 -0800683 with_console(req, stack_proc, to);
Jeff Dike3eddddc2005-09-16 19:27:46 -0700684}
Jeff Dike3eddddc2005-09-16 19:27:46 -0700685
Jeff Dikeae2587e2007-10-16 01:26:57 -0700686/*
687 * Changed by mconsole_setup, which is __setup, and called before SMP is
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 * active.
689 */
Jeff Diked50084a2006-01-06 00:18:50 -0800690static char *notify_socket = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691
Jeff Dike97a1fcb2007-07-23 18:43:48 -0700692static int __init mconsole_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693{
694 /* long to avoid size mismatch warnings from gcc */
695 long sock;
696 int err;
Balbir Singh36137122008-12-09 13:14:07 -0800697 char file[UNIX_PATH_MAX];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698
Jeff Dikeae2587e2007-10-16 01:26:57 -0700699 if (umid_file_name("mconsole", file, sizeof(file)))
700 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 snprintf(mconsole_socket_name, sizeof(file), "%s", file);
702
703 sock = os_create_unix_socket(file, sizeof(file), 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700704 if (sock < 0) {
705 printk(KERN_ERR "Failed to initialize management console\n");
706 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 }
Jeff Dike438ee672008-02-04 22:31:19 -0800708 if (os_set_fd_block(sock, 0))
709 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710
711 register_reboot_notifier(&reboot_notifier);
712
713 err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
Theodore Ts'oaab94462012-07-17 14:18:23 -0400714 IRQF_SHARED, "mconsole", (void *)sock);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700715 if (err) {
716 printk(KERN_ERR "Failed to get IRQ for management console\n");
Jeff Dike438ee672008-02-04 22:31:19 -0800717 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 }
719
Jeff Dikeae2587e2007-10-16 01:26:57 -0700720 if (notify_socket != NULL) {
Jeff Dike970d6e32006-01-06 00:18:48 -0800721 notify_socket = kstrdup(notify_socket, GFP_KERNEL);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700722 if (notify_socket != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 mconsole_notify(notify_socket, MCONSOLE_SOCKET,
Jeff Diked50084a2006-01-06 00:18:50 -0800724 mconsole_socket_name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 strlen(mconsole_socket_name) + 1);
726 else printk(KERN_ERR "mconsole_setup failed to strdup "
727 "string\n");
728 }
729
Jeff Dikeae2587e2007-10-16 01:26:57 -0700730 printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 MCONSOLE_VERSION, mconsole_socket_name);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700732 return 0;
Jeff Dike438ee672008-02-04 22:31:19 -0800733
734 out:
735 os_close_file(sock);
736 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737}
738
739__initcall(mconsole_init);
740
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800741static ssize_t mconsole_proc_write(struct file *file,
742 const char __user *buffer, size_t count, loff_t *pos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743{
744 char *buf;
745
Al Viro793b7962016-01-02 14:53:28 -0500746 buf = memdup_user_nul(buffer, count);
747 if (IS_ERR(buf))
748 return PTR_ERR(buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749
750 mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 kfree(buf);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700752 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753}
754
Alexey Dobriyan97a32532020-02-03 17:37:17 -0800755static const struct proc_ops mconsole_proc_ops = {
756 .proc_write = mconsole_proc_write,
757 .proc_lseek = noop_llseek,
Alexey Dobriyan6613c5e2009-12-14 18:00:11 -0800758};
759
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760static int create_proc_mconsole(void)
761{
762 struct proc_dir_entry *ent;
763
Jeff Dikeae2587e2007-10-16 01:26:57 -0700764 if (notify_socket == NULL)
765 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766
Alexey Dobriyan97a32532020-02-03 17:37:17 -0800767 ent = proc_create("mconsole", 0200, NULL, &mconsole_proc_ops);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700768 if (ent == NULL) {
David Howells4554eb92013-04-04 16:58:25 +0100769 printk(KERN_INFO "create_proc_mconsole : proc_create failed\n");
Jeff Dikeae2587e2007-10-16 01:26:57 -0700770 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 }
Jeff Dikeae2587e2007-10-16 01:26:57 -0700772 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773}
774
775static DEFINE_SPINLOCK(notify_spinlock);
776
777void lock_notify(void)
778{
779 spin_lock(&notify_spinlock);
780}
781
782void unlock_notify(void)
783{
784 spin_unlock(&notify_spinlock);
785}
786
787__initcall(create_proc_mconsole);
788
Jeff Dike088bec42007-10-16 01:27:20 -0700789#define NOTIFY "notify:"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
791static int mconsole_setup(char *str)
792{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700793 if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 str += strlen(NOTIFY);
795 notify_socket = str;
796 }
797 else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700798 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799}
800
Jeff Dike088bec42007-10-16 01:27:20 -0700801__setup("mconsole=", mconsole_setup);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802
803__uml_help(mconsole_setup,
804"mconsole=notify:<socket>\n"
805" Requests that the mconsole driver send a message to the named Unix\n"
806" socket containing the name of the mconsole socket. This also serves\n"
807" to notify outside processes when UML has booted far enough to respond\n"
808" to mconsole requests.\n\n"
809);
810
811static int notify_panic(struct notifier_block *self, unsigned long unused1,
812 void *ptr)
813{
814 char *message = ptr;
815
Jeff Dikeae2587e2007-10-16 01:26:57 -0700816 if (notify_socket == NULL)
817 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818
Jeff Diked50084a2006-01-06 00:18:50 -0800819 mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 strlen(message) + 1);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700821 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822}
823
824static struct notifier_block panic_exit_notifier = {
825 .notifier_call = notify_panic,
826 .next = NULL,
827 .priority = 1
828};
829
830static int add_notifier(void)
831{
Alan Sterne041c682006-03-27 01:16:30 -0800832 atomic_notifier_chain_register(&panic_notifier_list,
833 &panic_exit_notifier);
Jeff Dikeae2587e2007-10-16 01:26:57 -0700834 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835}
836
837__initcall(add_notifier);
838
839char *mconsole_notify_socket(void)
840{
Jeff Dikeae2587e2007-10-16 01:26:57 -0700841 return notify_socket;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842}
843
844EXPORT_SYMBOL(mconsole_notify_socket);