xref: /OK3568_Linux_fs/kernel/arch/um/drivers/mconsole_kern.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org)
4*4882a593Smuzhiyun  * Copyright (C) 2001 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com)
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include <linux/console.h>
8*4882a593Smuzhiyun #include <linux/ctype.h>
9*4882a593Smuzhiyun #include <linux/string.h>
10*4882a593Smuzhiyun #include <linux/interrupt.h>
11*4882a593Smuzhiyun #include <linux/list.h>
12*4882a593Smuzhiyun #include <linux/mm.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/notifier.h>
15*4882a593Smuzhiyun #include <linux/reboot.h>
16*4882a593Smuzhiyun #include <linux/sched/debug.h>
17*4882a593Smuzhiyun #include <linux/proc_fs.h>
18*4882a593Smuzhiyun #include <linux/slab.h>
19*4882a593Smuzhiyun #include <linux/syscalls.h>
20*4882a593Smuzhiyun #include <linux/utsname.h>
21*4882a593Smuzhiyun #include <linux/socket.h>
22*4882a593Smuzhiyun #include <linux/un.h>
23*4882a593Smuzhiyun #include <linux/workqueue.h>
24*4882a593Smuzhiyun #include <linux/mutex.h>
25*4882a593Smuzhiyun #include <linux/fs.h>
26*4882a593Smuzhiyun #include <linux/mount.h>
27*4882a593Smuzhiyun #include <linux/file.h>
28*4882a593Smuzhiyun #include <linux/uaccess.h>
29*4882a593Smuzhiyun #include <asm/switch_to.h>
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun #include <init.h>
32*4882a593Smuzhiyun #include <irq_kern.h>
33*4882a593Smuzhiyun #include <irq_user.h>
34*4882a593Smuzhiyun #include <kern_util.h>
35*4882a593Smuzhiyun #include "mconsole.h"
36*4882a593Smuzhiyun #include "mconsole_kern.h"
37*4882a593Smuzhiyun #include <os.h>
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun static struct vfsmount *proc_mnt = NULL;
40*4882a593Smuzhiyun 
do_unlink_socket(struct notifier_block * notifier,unsigned long what,void * data)41*4882a593Smuzhiyun static int do_unlink_socket(struct notifier_block *notifier,
42*4882a593Smuzhiyun 			    unsigned long what, void *data)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun 	return mconsole_unlink_socket();
45*4882a593Smuzhiyun }
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun static struct notifier_block reboot_notifier = {
49*4882a593Smuzhiyun 	.notifier_call		= do_unlink_socket,
50*4882a593Smuzhiyun 	.priority		= 0,
51*4882a593Smuzhiyun };
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun /* Safe without explicit locking for now.  Tasklets provide their own
54*4882a593Smuzhiyun  * locking, and the interrupt handler is safe because it can't interrupt
55*4882a593Smuzhiyun  * itself and it can only happen on CPU 0.
56*4882a593Smuzhiyun  */
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun static LIST_HEAD(mc_requests);
59*4882a593Smuzhiyun 
mc_work_proc(struct work_struct * unused)60*4882a593Smuzhiyun static void mc_work_proc(struct work_struct *unused)
61*4882a593Smuzhiyun {
62*4882a593Smuzhiyun 	struct mconsole_entry *req;
63*4882a593Smuzhiyun 	unsigned long flags;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	while (!list_empty(&mc_requests)) {
66*4882a593Smuzhiyun 		local_irq_save(flags);
67*4882a593Smuzhiyun 		req = list_entry(mc_requests.next, struct mconsole_entry, list);
68*4882a593Smuzhiyun 		list_del(&req->list);
69*4882a593Smuzhiyun 		local_irq_restore(flags);
70*4882a593Smuzhiyun 		req->request.cmd->handler(&req->request);
71*4882a593Smuzhiyun 		kfree(req);
72*4882a593Smuzhiyun 	}
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun static DECLARE_WORK(mconsole_work, mc_work_proc);
76*4882a593Smuzhiyun 
mconsole_interrupt(int irq,void * dev_id)77*4882a593Smuzhiyun static irqreturn_t mconsole_interrupt(int irq, void *dev_id)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun 	/* long to avoid size mismatch warnings from gcc */
80*4882a593Smuzhiyun 	long fd;
81*4882a593Smuzhiyun 	struct mconsole_entry *new;
82*4882a593Smuzhiyun 	static struct mc_request req;	/* that's OK */
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	fd = (long) dev_id;
85*4882a593Smuzhiyun 	while (mconsole_get_request(fd, &req)) {
86*4882a593Smuzhiyun 		if (req.cmd->context == MCONSOLE_INTR)
87*4882a593Smuzhiyun 			(*req.cmd->handler)(&req);
88*4882a593Smuzhiyun 		else {
89*4882a593Smuzhiyun 			new = kmalloc(sizeof(*new), GFP_NOWAIT);
90*4882a593Smuzhiyun 			if (new == NULL)
91*4882a593Smuzhiyun 				mconsole_reply(&req, "Out of memory", 1, 0);
92*4882a593Smuzhiyun 			else {
93*4882a593Smuzhiyun 				new->request = req;
94*4882a593Smuzhiyun 				new->request.regs = get_irq_regs()->regs;
95*4882a593Smuzhiyun 				list_add(&new->list, &mc_requests);
96*4882a593Smuzhiyun 			}
97*4882a593Smuzhiyun 		}
98*4882a593Smuzhiyun 	}
99*4882a593Smuzhiyun 	if (!list_empty(&mc_requests))
100*4882a593Smuzhiyun 		schedule_work(&mconsole_work);
101*4882a593Smuzhiyun 	return IRQ_HANDLED;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun 
mconsole_version(struct mc_request * req)104*4882a593Smuzhiyun void mconsole_version(struct mc_request *req)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun 	char version[256];
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	sprintf(version, "%s %s %s %s %s", utsname()->sysname,
109*4882a593Smuzhiyun 		utsname()->nodename, utsname()->release, utsname()->version,
110*4882a593Smuzhiyun 		utsname()->machine);
111*4882a593Smuzhiyun 	mconsole_reply(req, version, 0, 0);
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun 
mconsole_log(struct mc_request * req)114*4882a593Smuzhiyun void mconsole_log(struct mc_request *req)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun 	int len;
117*4882a593Smuzhiyun 	char *ptr = req->request.data;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	ptr += strlen("log ");
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	len = req->len - (ptr - req->request.data);
122*4882a593Smuzhiyun 	printk(KERN_WARNING "%.*s", len, ptr);
123*4882a593Smuzhiyun 	mconsole_reply(req, "", 0, 0);
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun 
mconsole_proc(struct mc_request * req)126*4882a593Smuzhiyun void mconsole_proc(struct mc_request *req)
127*4882a593Smuzhiyun {
128*4882a593Smuzhiyun 	struct vfsmount *mnt = proc_mnt;
129*4882a593Smuzhiyun 	char *buf;
130*4882a593Smuzhiyun 	int len;
131*4882a593Smuzhiyun 	struct file *file;
132*4882a593Smuzhiyun 	int first_chunk = 1;
133*4882a593Smuzhiyun 	char *ptr = req->request.data;
134*4882a593Smuzhiyun 	loff_t pos = 0;
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 	ptr += strlen("proc");
137*4882a593Smuzhiyun 	ptr = skip_spaces(ptr);
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	if (!mnt) {
140*4882a593Smuzhiyun 		mconsole_reply(req, "Proc not available", 1, 0);
141*4882a593Smuzhiyun 		goto out;
142*4882a593Smuzhiyun 	}
143*4882a593Smuzhiyun 	file = file_open_root(mnt->mnt_root, mnt, ptr, O_RDONLY, 0);
144*4882a593Smuzhiyun 	if (IS_ERR(file)) {
145*4882a593Smuzhiyun 		mconsole_reply(req, "Failed to open file", 1, 0);
146*4882a593Smuzhiyun 		printk(KERN_ERR "open /proc/%s: %ld\n", ptr, PTR_ERR(file));
147*4882a593Smuzhiyun 		goto out;
148*4882a593Smuzhiyun 	}
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
151*4882a593Smuzhiyun 	if (buf == NULL) {
152*4882a593Smuzhiyun 		mconsole_reply(req, "Failed to allocate buffer", 1, 0);
153*4882a593Smuzhiyun 		goto out_fput;
154*4882a593Smuzhiyun 	}
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	do {
157*4882a593Smuzhiyun 		len = kernel_read(file, buf, PAGE_SIZE - 1, &pos);
158*4882a593Smuzhiyun 		if (len < 0) {
159*4882a593Smuzhiyun 			mconsole_reply(req, "Read of file failed", 1, 0);
160*4882a593Smuzhiyun 			goto out_free;
161*4882a593Smuzhiyun 		}
162*4882a593Smuzhiyun 		/* Begin the file content on his own line. */
163*4882a593Smuzhiyun 		if (first_chunk) {
164*4882a593Smuzhiyun 			mconsole_reply(req, "\n", 0, 1);
165*4882a593Smuzhiyun 			first_chunk = 0;
166*4882a593Smuzhiyun 		}
167*4882a593Smuzhiyun 		buf[len] = '\0';
168*4882a593Smuzhiyun 		mconsole_reply(req, buf, 0, (len > 0));
169*4882a593Smuzhiyun 	} while (len > 0);
170*4882a593Smuzhiyun  out_free:
171*4882a593Smuzhiyun 	kfree(buf);
172*4882a593Smuzhiyun  out_fput:
173*4882a593Smuzhiyun 	fput(file);
174*4882a593Smuzhiyun  out: ;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun #define UML_MCONSOLE_HELPTEXT \
178*4882a593Smuzhiyun "Commands: \n\
179*4882a593Smuzhiyun     version - Get kernel version \n\
180*4882a593Smuzhiyun     help - Print this message \n\
181*4882a593Smuzhiyun     halt - Halt UML \n\
182*4882a593Smuzhiyun     reboot - Reboot UML \n\
183*4882a593Smuzhiyun     config <dev>=<config> - Add a new device to UML;  \n\
184*4882a593Smuzhiyun 	same syntax as command line \n\
185*4882a593Smuzhiyun     config <dev> - Query the configuration of a device \n\
186*4882a593Smuzhiyun     remove <dev> - Remove a device from UML \n\
187*4882a593Smuzhiyun     sysrq <letter> - Performs the SysRq action controlled by the letter \n\
188*4882a593Smuzhiyun     cad - invoke the Ctrl-Alt-Del handler \n\
189*4882a593Smuzhiyun     stop - pause the UML; it will do nothing until it receives a 'go' \n\
190*4882a593Smuzhiyun     go - continue the UML after a 'stop' \n\
191*4882a593Smuzhiyun     log <string> - make UML enter <string> into the kernel log\n\
192*4882a593Smuzhiyun     proc <file> - returns the contents of the UML's /proc/<file>\n\
193*4882a593Smuzhiyun     stack <pid> - returns the stack of the specified pid\n\
194*4882a593Smuzhiyun "
195*4882a593Smuzhiyun 
mconsole_help(struct mc_request * req)196*4882a593Smuzhiyun void mconsole_help(struct mc_request *req)
197*4882a593Smuzhiyun {
198*4882a593Smuzhiyun 	mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0);
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun 
mconsole_halt(struct mc_request * req)201*4882a593Smuzhiyun void mconsole_halt(struct mc_request *req)
202*4882a593Smuzhiyun {
203*4882a593Smuzhiyun 	mconsole_reply(req, "", 0, 0);
204*4882a593Smuzhiyun 	machine_halt();
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun 
mconsole_reboot(struct mc_request * req)207*4882a593Smuzhiyun void mconsole_reboot(struct mc_request *req)
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun 	mconsole_reply(req, "", 0, 0);
210*4882a593Smuzhiyun 	machine_restart(NULL);
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun 
mconsole_cad(struct mc_request * req)213*4882a593Smuzhiyun void mconsole_cad(struct mc_request *req)
214*4882a593Smuzhiyun {
215*4882a593Smuzhiyun 	mconsole_reply(req, "", 0, 0);
216*4882a593Smuzhiyun 	ctrl_alt_del();
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun 
mconsole_go(struct mc_request * req)219*4882a593Smuzhiyun void mconsole_go(struct mc_request *req)
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun 	mconsole_reply(req, "Not stopped", 1, 0);
222*4882a593Smuzhiyun }
223*4882a593Smuzhiyun 
mconsole_stop(struct mc_request * req)224*4882a593Smuzhiyun void mconsole_stop(struct mc_request *req)
225*4882a593Smuzhiyun {
226*4882a593Smuzhiyun 	block_signals();
227*4882a593Smuzhiyun 	os_set_fd_block(req->originating_fd, 1);
228*4882a593Smuzhiyun 	mconsole_reply(req, "stopped", 0, 0);
229*4882a593Smuzhiyun 	for (;;) {
230*4882a593Smuzhiyun 		if (!mconsole_get_request(req->originating_fd, req))
231*4882a593Smuzhiyun 			continue;
232*4882a593Smuzhiyun 		if (req->cmd->handler == mconsole_go)
233*4882a593Smuzhiyun 			break;
234*4882a593Smuzhiyun 		if (req->cmd->handler == mconsole_stop) {
235*4882a593Smuzhiyun 			mconsole_reply(req, "Already stopped", 1, 0);
236*4882a593Smuzhiyun 			continue;
237*4882a593Smuzhiyun 		}
238*4882a593Smuzhiyun 		if (req->cmd->handler == mconsole_sysrq) {
239*4882a593Smuzhiyun 			struct pt_regs *old_regs;
240*4882a593Smuzhiyun 			old_regs = set_irq_regs((struct pt_regs *)&req->regs);
241*4882a593Smuzhiyun 			mconsole_sysrq(req);
242*4882a593Smuzhiyun 			set_irq_regs(old_regs);
243*4882a593Smuzhiyun 			continue;
244*4882a593Smuzhiyun 		}
245*4882a593Smuzhiyun 		(*req->cmd->handler)(req);
246*4882a593Smuzhiyun 	}
247*4882a593Smuzhiyun 	os_set_fd_block(req->originating_fd, 0);
248*4882a593Smuzhiyun 	mconsole_reply(req, "", 0, 0);
249*4882a593Smuzhiyun 	unblock_signals();
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun 
252*4882a593Smuzhiyun static DEFINE_SPINLOCK(mc_devices_lock);
253*4882a593Smuzhiyun static LIST_HEAD(mconsole_devices);
254*4882a593Smuzhiyun 
mconsole_register_dev(struct mc_device * new)255*4882a593Smuzhiyun void mconsole_register_dev(struct mc_device *new)
256*4882a593Smuzhiyun {
257*4882a593Smuzhiyun 	spin_lock(&mc_devices_lock);
258*4882a593Smuzhiyun 	BUG_ON(!list_empty(&new->list));
259*4882a593Smuzhiyun 	list_add(&new->list, &mconsole_devices);
260*4882a593Smuzhiyun 	spin_unlock(&mc_devices_lock);
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun 
mconsole_find_dev(char * name)263*4882a593Smuzhiyun static struct mc_device *mconsole_find_dev(char *name)
264*4882a593Smuzhiyun {
265*4882a593Smuzhiyun 	struct list_head *ele;
266*4882a593Smuzhiyun 	struct mc_device *dev;
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	list_for_each(ele, &mconsole_devices) {
269*4882a593Smuzhiyun 		dev = list_entry(ele, struct mc_device, list);
270*4882a593Smuzhiyun 		if (!strncmp(name, dev->name, strlen(dev->name)))
271*4882a593Smuzhiyun 			return dev;
272*4882a593Smuzhiyun 	}
273*4882a593Smuzhiyun 	return NULL;
274*4882a593Smuzhiyun }
275*4882a593Smuzhiyun 
276*4882a593Smuzhiyun #define UNPLUGGED_PER_PAGE \
277*4882a593Smuzhiyun 	((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun struct unplugged_pages {
280*4882a593Smuzhiyun 	struct list_head list;
281*4882a593Smuzhiyun 	void *pages[UNPLUGGED_PER_PAGE];
282*4882a593Smuzhiyun };
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun static DEFINE_MUTEX(plug_mem_mutex);
285*4882a593Smuzhiyun static unsigned long long unplugged_pages_count = 0;
286*4882a593Smuzhiyun static LIST_HEAD(unplugged_pages);
287*4882a593Smuzhiyun static int unplug_index = UNPLUGGED_PER_PAGE;
288*4882a593Smuzhiyun 
mem_config(char * str,char ** error_out)289*4882a593Smuzhiyun static int mem_config(char *str, char **error_out)
290*4882a593Smuzhiyun {
291*4882a593Smuzhiyun 	unsigned long long diff;
292*4882a593Smuzhiyun 	int err = -EINVAL, i, add;
293*4882a593Smuzhiyun 	char *ret;
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 	if (str[0] != '=') {
296*4882a593Smuzhiyun 		*error_out = "Expected '=' after 'mem'";
297*4882a593Smuzhiyun 		goto out;
298*4882a593Smuzhiyun 	}
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	str++;
301*4882a593Smuzhiyun 	if (str[0] == '-')
302*4882a593Smuzhiyun 		add = 0;
303*4882a593Smuzhiyun 	else if (str[0] == '+') {
304*4882a593Smuzhiyun 		add = 1;
305*4882a593Smuzhiyun 	}
306*4882a593Smuzhiyun 	else {
307*4882a593Smuzhiyun 		*error_out = "Expected increment to start with '-' or '+'";
308*4882a593Smuzhiyun 		goto out;
309*4882a593Smuzhiyun 	}
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 	str++;
312*4882a593Smuzhiyun 	diff = memparse(str, &ret);
313*4882a593Smuzhiyun 	if (*ret != '\0') {
314*4882a593Smuzhiyun 		*error_out = "Failed to parse memory increment";
315*4882a593Smuzhiyun 		goto out;
316*4882a593Smuzhiyun 	}
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	diff /= PAGE_SIZE;
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	mutex_lock(&plug_mem_mutex);
321*4882a593Smuzhiyun 	for (i = 0; i < diff; i++) {
322*4882a593Smuzhiyun 		struct unplugged_pages *unplugged;
323*4882a593Smuzhiyun 		void *addr;
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun 		if (add) {
326*4882a593Smuzhiyun 			if (list_empty(&unplugged_pages))
327*4882a593Smuzhiyun 				break;
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 			unplugged = list_entry(unplugged_pages.next,
330*4882a593Smuzhiyun 					       struct unplugged_pages, list);
331*4882a593Smuzhiyun 			if (unplug_index > 0)
332*4882a593Smuzhiyun 				addr = unplugged->pages[--unplug_index];
333*4882a593Smuzhiyun 			else {
334*4882a593Smuzhiyun 				list_del(&unplugged->list);
335*4882a593Smuzhiyun 				addr = unplugged;
336*4882a593Smuzhiyun 				unplug_index = UNPLUGGED_PER_PAGE;
337*4882a593Smuzhiyun 			}
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 			free_page((unsigned long) addr);
340*4882a593Smuzhiyun 			unplugged_pages_count--;
341*4882a593Smuzhiyun 		}
342*4882a593Smuzhiyun 		else {
343*4882a593Smuzhiyun 			struct page *page;
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 			page = alloc_page(GFP_ATOMIC);
346*4882a593Smuzhiyun 			if (page == NULL)
347*4882a593Smuzhiyun 				break;
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 			unplugged = page_address(page);
350*4882a593Smuzhiyun 			if (unplug_index == UNPLUGGED_PER_PAGE) {
351*4882a593Smuzhiyun 				list_add(&unplugged->list, &unplugged_pages);
352*4882a593Smuzhiyun 				unplug_index = 0;
353*4882a593Smuzhiyun 			}
354*4882a593Smuzhiyun 			else {
355*4882a593Smuzhiyun 				struct list_head *entry = unplugged_pages.next;
356*4882a593Smuzhiyun 				addr = unplugged;
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 				unplugged = list_entry(entry,
359*4882a593Smuzhiyun 						       struct unplugged_pages,
360*4882a593Smuzhiyun 						       list);
361*4882a593Smuzhiyun 				err = os_drop_memory(addr, PAGE_SIZE);
362*4882a593Smuzhiyun 				if (err) {
363*4882a593Smuzhiyun 					printk(KERN_ERR "Failed to release "
364*4882a593Smuzhiyun 					       "memory - errno = %d\n", err);
365*4882a593Smuzhiyun 					*error_out = "Failed to release memory";
366*4882a593Smuzhiyun 					goto out_unlock;
367*4882a593Smuzhiyun 				}
368*4882a593Smuzhiyun 				unplugged->pages[unplug_index++] = addr;
369*4882a593Smuzhiyun 			}
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 			unplugged_pages_count++;
372*4882a593Smuzhiyun 		}
373*4882a593Smuzhiyun 	}
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 	err = 0;
376*4882a593Smuzhiyun out_unlock:
377*4882a593Smuzhiyun 	mutex_unlock(&plug_mem_mutex);
378*4882a593Smuzhiyun out:
379*4882a593Smuzhiyun 	return err;
380*4882a593Smuzhiyun }
381*4882a593Smuzhiyun 
mem_get_config(char * name,char * str,int size,char ** error_out)382*4882a593Smuzhiyun static int mem_get_config(char *name, char *str, int size, char **error_out)
383*4882a593Smuzhiyun {
384*4882a593Smuzhiyun 	char buf[sizeof("18446744073709551615")];
385*4882a593Smuzhiyun 	int len = 0;
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun 	sprintf(buf, "%ld", uml_physmem);
388*4882a593Smuzhiyun 	CONFIG_CHUNK(str, size, len, buf, 1);
389*4882a593Smuzhiyun 
390*4882a593Smuzhiyun 	return len;
391*4882a593Smuzhiyun }
392*4882a593Smuzhiyun 
mem_id(char ** str,int * start_out,int * end_out)393*4882a593Smuzhiyun static int mem_id(char **str, int *start_out, int *end_out)
394*4882a593Smuzhiyun {
395*4882a593Smuzhiyun 	*start_out = 0;
396*4882a593Smuzhiyun 	*end_out = 0;
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 	return 0;
399*4882a593Smuzhiyun }
400*4882a593Smuzhiyun 
mem_remove(int n,char ** error_out)401*4882a593Smuzhiyun static int mem_remove(int n, char **error_out)
402*4882a593Smuzhiyun {
403*4882a593Smuzhiyun 	*error_out = "Memory doesn't support the remove operation";
404*4882a593Smuzhiyun 	return -EBUSY;
405*4882a593Smuzhiyun }
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun static struct mc_device mem_mc = {
408*4882a593Smuzhiyun 	.list		= LIST_HEAD_INIT(mem_mc.list),
409*4882a593Smuzhiyun 	.name		= "mem",
410*4882a593Smuzhiyun 	.config		= mem_config,
411*4882a593Smuzhiyun 	.get_config	= mem_get_config,
412*4882a593Smuzhiyun 	.id		= mem_id,
413*4882a593Smuzhiyun 	.remove		= mem_remove,
414*4882a593Smuzhiyun };
415*4882a593Smuzhiyun 
mem_mc_init(void)416*4882a593Smuzhiyun static int __init mem_mc_init(void)
417*4882a593Smuzhiyun {
418*4882a593Smuzhiyun 	if (can_drop_memory())
419*4882a593Smuzhiyun 		mconsole_register_dev(&mem_mc);
420*4882a593Smuzhiyun 	else printk(KERN_ERR "Can't release memory to the host - memory "
421*4882a593Smuzhiyun 		    "hotplug won't be supported\n");
422*4882a593Smuzhiyun 	return 0;
423*4882a593Smuzhiyun }
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun __initcall(mem_mc_init);
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun #define CONFIG_BUF_SIZE 64
428*4882a593Smuzhiyun 
mconsole_get_config(int (* get_config)(char *,char *,int,char **),struct mc_request * req,char * name)429*4882a593Smuzhiyun static void mconsole_get_config(int (*get_config)(char *, char *, int,
430*4882a593Smuzhiyun 						  char **),
431*4882a593Smuzhiyun 				struct mc_request *req, char *name)
432*4882a593Smuzhiyun {
433*4882a593Smuzhiyun 	char default_buf[CONFIG_BUF_SIZE], *error, *buf;
434*4882a593Smuzhiyun 	int n, size;
435*4882a593Smuzhiyun 
436*4882a593Smuzhiyun 	if (get_config == NULL) {
437*4882a593Smuzhiyun 		mconsole_reply(req, "No get_config routine defined", 1, 0);
438*4882a593Smuzhiyun 		return;
439*4882a593Smuzhiyun 	}
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 	error = NULL;
442*4882a593Smuzhiyun 	size = ARRAY_SIZE(default_buf);
443*4882a593Smuzhiyun 	buf = default_buf;
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	while (1) {
446*4882a593Smuzhiyun 		n = (*get_config)(name, buf, size, &error);
447*4882a593Smuzhiyun 		if (error != NULL) {
448*4882a593Smuzhiyun 			mconsole_reply(req, error, 1, 0);
449*4882a593Smuzhiyun 			goto out;
450*4882a593Smuzhiyun 		}
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 		if (n <= size) {
453*4882a593Smuzhiyun 			mconsole_reply(req, buf, 0, 0);
454*4882a593Smuzhiyun 			goto out;
455*4882a593Smuzhiyun 		}
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 		if (buf != default_buf)
458*4882a593Smuzhiyun 			kfree(buf);
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 		size = n;
461*4882a593Smuzhiyun 		buf = kmalloc(size, GFP_KERNEL);
462*4882a593Smuzhiyun 		if (buf == NULL) {
463*4882a593Smuzhiyun 			mconsole_reply(req, "Failed to allocate buffer", 1, 0);
464*4882a593Smuzhiyun 			return;
465*4882a593Smuzhiyun 		}
466*4882a593Smuzhiyun 	}
467*4882a593Smuzhiyun  out:
468*4882a593Smuzhiyun 	if (buf != default_buf)
469*4882a593Smuzhiyun 		kfree(buf);
470*4882a593Smuzhiyun }
471*4882a593Smuzhiyun 
mconsole_config(struct mc_request * req)472*4882a593Smuzhiyun void mconsole_config(struct mc_request *req)
473*4882a593Smuzhiyun {
474*4882a593Smuzhiyun 	struct mc_device *dev;
475*4882a593Smuzhiyun 	char *ptr = req->request.data, *name, *error_string = "";
476*4882a593Smuzhiyun 	int err;
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun 	ptr += strlen("config");
479*4882a593Smuzhiyun 	ptr = skip_spaces(ptr);
480*4882a593Smuzhiyun 	dev = mconsole_find_dev(ptr);
481*4882a593Smuzhiyun 	if (dev == NULL) {
482*4882a593Smuzhiyun 		mconsole_reply(req, "Bad configuration option", 1, 0);
483*4882a593Smuzhiyun 		return;
484*4882a593Smuzhiyun 	}
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun 	name = &ptr[strlen(dev->name)];
487*4882a593Smuzhiyun 	ptr = name;
488*4882a593Smuzhiyun 	while ((*ptr != '=') && (*ptr != '\0'))
489*4882a593Smuzhiyun 		ptr++;
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	if (*ptr == '=') {
492*4882a593Smuzhiyun 		err = (*dev->config)(name, &error_string);
493*4882a593Smuzhiyun 		mconsole_reply(req, error_string, err, 0);
494*4882a593Smuzhiyun 	}
495*4882a593Smuzhiyun 	else mconsole_get_config(dev->get_config, req, name);
496*4882a593Smuzhiyun }
497*4882a593Smuzhiyun 
mconsole_remove(struct mc_request * req)498*4882a593Smuzhiyun void mconsole_remove(struct mc_request *req)
499*4882a593Smuzhiyun {
500*4882a593Smuzhiyun 	struct mc_device *dev;
501*4882a593Smuzhiyun 	char *ptr = req->request.data, *err_msg = "";
502*4882a593Smuzhiyun 	char error[256];
503*4882a593Smuzhiyun 	int err, start, end, n;
504*4882a593Smuzhiyun 
505*4882a593Smuzhiyun 	ptr += strlen("remove");
506*4882a593Smuzhiyun 	ptr = skip_spaces(ptr);
507*4882a593Smuzhiyun 	dev = mconsole_find_dev(ptr);
508*4882a593Smuzhiyun 	if (dev == NULL) {
509*4882a593Smuzhiyun 		mconsole_reply(req, "Bad remove option", 1, 0);
510*4882a593Smuzhiyun 		return;
511*4882a593Smuzhiyun 	}
512*4882a593Smuzhiyun 
513*4882a593Smuzhiyun 	ptr = &ptr[strlen(dev->name)];
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun 	err = 1;
516*4882a593Smuzhiyun 	n = (*dev->id)(&ptr, &start, &end);
517*4882a593Smuzhiyun 	if (n < 0) {
518*4882a593Smuzhiyun 		err_msg = "Couldn't parse device number";
519*4882a593Smuzhiyun 		goto out;
520*4882a593Smuzhiyun 	}
521*4882a593Smuzhiyun 	else if ((n < start) || (n > end)) {
522*4882a593Smuzhiyun 		sprintf(error, "Invalid device number - must be between "
523*4882a593Smuzhiyun 			"%d and %d", start, end);
524*4882a593Smuzhiyun 		err_msg = error;
525*4882a593Smuzhiyun 		goto out;
526*4882a593Smuzhiyun 	}
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	err_msg = NULL;
529*4882a593Smuzhiyun 	err = (*dev->remove)(n, &err_msg);
530*4882a593Smuzhiyun 	switch(err) {
531*4882a593Smuzhiyun 	case 0:
532*4882a593Smuzhiyun 		err_msg = "";
533*4882a593Smuzhiyun 		break;
534*4882a593Smuzhiyun 	case -ENODEV:
535*4882a593Smuzhiyun 		if (err_msg == NULL)
536*4882a593Smuzhiyun 			err_msg = "Device doesn't exist";
537*4882a593Smuzhiyun 		break;
538*4882a593Smuzhiyun 	case -EBUSY:
539*4882a593Smuzhiyun 		if (err_msg == NULL)
540*4882a593Smuzhiyun 			err_msg = "Device is currently open";
541*4882a593Smuzhiyun 		break;
542*4882a593Smuzhiyun 	default:
543*4882a593Smuzhiyun 		break;
544*4882a593Smuzhiyun 	}
545*4882a593Smuzhiyun out:
546*4882a593Smuzhiyun 	mconsole_reply(req, err_msg, err, 0);
547*4882a593Smuzhiyun }
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun struct mconsole_output {
550*4882a593Smuzhiyun 	struct list_head list;
551*4882a593Smuzhiyun 	struct mc_request *req;
552*4882a593Smuzhiyun };
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun static DEFINE_SPINLOCK(client_lock);
555*4882a593Smuzhiyun static LIST_HEAD(clients);
556*4882a593Smuzhiyun static char console_buf[MCONSOLE_MAX_DATA];
557*4882a593Smuzhiyun 
console_write(struct console * console,const char * string,unsigned int len)558*4882a593Smuzhiyun static void console_write(struct console *console, const char *string,
559*4882a593Smuzhiyun 			  unsigned int len)
560*4882a593Smuzhiyun {
561*4882a593Smuzhiyun 	struct list_head *ele;
562*4882a593Smuzhiyun 	int n;
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	if (list_empty(&clients))
565*4882a593Smuzhiyun 		return;
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 	while (len > 0) {
568*4882a593Smuzhiyun 		n = min((size_t) len, ARRAY_SIZE(console_buf));
569*4882a593Smuzhiyun 		strncpy(console_buf, string, n);
570*4882a593Smuzhiyun 		string += n;
571*4882a593Smuzhiyun 		len -= n;
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun 		list_for_each(ele, &clients) {
574*4882a593Smuzhiyun 			struct mconsole_output *entry;
575*4882a593Smuzhiyun 
576*4882a593Smuzhiyun 			entry = list_entry(ele, struct mconsole_output, list);
577*4882a593Smuzhiyun 			mconsole_reply_len(entry->req, console_buf, n, 0, 1);
578*4882a593Smuzhiyun 		}
579*4882a593Smuzhiyun 	}
580*4882a593Smuzhiyun }
581*4882a593Smuzhiyun 
582*4882a593Smuzhiyun static struct console mc_console = { .name	= "mc",
583*4882a593Smuzhiyun 				     .write	= console_write,
584*4882a593Smuzhiyun 				     .flags	= CON_ENABLED,
585*4882a593Smuzhiyun 				     .index	= -1 };
586*4882a593Smuzhiyun 
mc_add_console(void)587*4882a593Smuzhiyun static int mc_add_console(void)
588*4882a593Smuzhiyun {
589*4882a593Smuzhiyun 	register_console(&mc_console);
590*4882a593Smuzhiyun 	return 0;
591*4882a593Smuzhiyun }
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun late_initcall(mc_add_console);
594*4882a593Smuzhiyun 
with_console(struct mc_request * req,void (* proc)(void *),void * arg)595*4882a593Smuzhiyun static void with_console(struct mc_request *req, void (*proc)(void *),
596*4882a593Smuzhiyun 			 void *arg)
597*4882a593Smuzhiyun {
598*4882a593Smuzhiyun 	struct mconsole_output entry;
599*4882a593Smuzhiyun 	unsigned long flags;
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	entry.req = req;
602*4882a593Smuzhiyun 	spin_lock_irqsave(&client_lock, flags);
603*4882a593Smuzhiyun 	list_add(&entry.list, &clients);
604*4882a593Smuzhiyun 	spin_unlock_irqrestore(&client_lock, flags);
605*4882a593Smuzhiyun 
606*4882a593Smuzhiyun 	(*proc)(arg);
607*4882a593Smuzhiyun 
608*4882a593Smuzhiyun 	mconsole_reply_len(req, "", 0, 0, 0);
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun 	spin_lock_irqsave(&client_lock, flags);
611*4882a593Smuzhiyun 	list_del(&entry.list);
612*4882a593Smuzhiyun 	spin_unlock_irqrestore(&client_lock, flags);
613*4882a593Smuzhiyun }
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun #ifdef CONFIG_MAGIC_SYSRQ
616*4882a593Smuzhiyun 
617*4882a593Smuzhiyun #include <linux/sysrq.h>
618*4882a593Smuzhiyun 
sysrq_proc(void * arg)619*4882a593Smuzhiyun static void sysrq_proc(void *arg)
620*4882a593Smuzhiyun {
621*4882a593Smuzhiyun 	char *op = arg;
622*4882a593Smuzhiyun 	handle_sysrq(*op);
623*4882a593Smuzhiyun }
624*4882a593Smuzhiyun 
mconsole_sysrq(struct mc_request * req)625*4882a593Smuzhiyun void mconsole_sysrq(struct mc_request *req)
626*4882a593Smuzhiyun {
627*4882a593Smuzhiyun 	char *ptr = req->request.data;
628*4882a593Smuzhiyun 
629*4882a593Smuzhiyun 	ptr += strlen("sysrq");
630*4882a593Smuzhiyun 	ptr = skip_spaces(ptr);
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun 	/*
633*4882a593Smuzhiyun 	 * With 'b', the system will shut down without a chance to reply,
634*4882a593Smuzhiyun 	 * so in this case, we reply first.
635*4882a593Smuzhiyun 	 */
636*4882a593Smuzhiyun 	if (*ptr == 'b')
637*4882a593Smuzhiyun 		mconsole_reply(req, "", 0, 0);
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun 	with_console(req, sysrq_proc, ptr);
640*4882a593Smuzhiyun }
641*4882a593Smuzhiyun #else
mconsole_sysrq(struct mc_request * req)642*4882a593Smuzhiyun void mconsole_sysrq(struct mc_request *req)
643*4882a593Smuzhiyun {
644*4882a593Smuzhiyun 	mconsole_reply(req, "Sysrq not compiled in", 1, 0);
645*4882a593Smuzhiyun }
646*4882a593Smuzhiyun #endif
647*4882a593Smuzhiyun 
stack_proc(void * arg)648*4882a593Smuzhiyun static void stack_proc(void *arg)
649*4882a593Smuzhiyun {
650*4882a593Smuzhiyun 	struct task_struct *task = arg;
651*4882a593Smuzhiyun 
652*4882a593Smuzhiyun 	show_stack(task, NULL, KERN_INFO);
653*4882a593Smuzhiyun }
654*4882a593Smuzhiyun 
655*4882a593Smuzhiyun /*
656*4882a593Smuzhiyun  * Mconsole stack trace
657*4882a593Smuzhiyun  *  Added by Allan Graves, Jeff Dike
658*4882a593Smuzhiyun  *  Dumps a stacks registers to the linux console.
659*4882a593Smuzhiyun  *  Usage stack <pid>.
660*4882a593Smuzhiyun  */
mconsole_stack(struct mc_request * req)661*4882a593Smuzhiyun void mconsole_stack(struct mc_request *req)
662*4882a593Smuzhiyun {
663*4882a593Smuzhiyun 	char *ptr = req->request.data;
664*4882a593Smuzhiyun 	int pid_requested= -1;
665*4882a593Smuzhiyun 	struct task_struct *to = NULL;
666*4882a593Smuzhiyun 
667*4882a593Smuzhiyun 	/*
668*4882a593Smuzhiyun 	 * Would be nice:
669*4882a593Smuzhiyun 	 * 1) Send showregs output to mconsole.
670*4882a593Smuzhiyun 	 * 2) Add a way to stack dump all pids.
671*4882a593Smuzhiyun 	 */
672*4882a593Smuzhiyun 
673*4882a593Smuzhiyun 	ptr += strlen("stack");
674*4882a593Smuzhiyun 	ptr = skip_spaces(ptr);
675*4882a593Smuzhiyun 
676*4882a593Smuzhiyun 	/*
677*4882a593Smuzhiyun 	 * Should really check for multiple pids or reject bad args here
678*4882a593Smuzhiyun 	 */
679*4882a593Smuzhiyun 	/* What do the arguments in mconsole_reply mean? */
680*4882a593Smuzhiyun 	if (sscanf(ptr, "%d", &pid_requested) == 0) {
681*4882a593Smuzhiyun 		mconsole_reply(req, "Please specify a pid", 1, 0);
682*4882a593Smuzhiyun 		return;
683*4882a593Smuzhiyun 	}
684*4882a593Smuzhiyun 
685*4882a593Smuzhiyun 	to = find_task_by_pid_ns(pid_requested, &init_pid_ns);
686*4882a593Smuzhiyun 	if ((to == NULL) || (pid_requested == 0)) {
687*4882a593Smuzhiyun 		mconsole_reply(req, "Couldn't find that pid", 1, 0);
688*4882a593Smuzhiyun 		return;
689*4882a593Smuzhiyun 	}
690*4882a593Smuzhiyun 	with_console(req, stack_proc, to);
691*4882a593Smuzhiyun }
692*4882a593Smuzhiyun 
mount_proc(void)693*4882a593Smuzhiyun static int __init mount_proc(void)
694*4882a593Smuzhiyun {
695*4882a593Smuzhiyun 	struct file_system_type *proc_fs_type;
696*4882a593Smuzhiyun 	struct vfsmount *mnt;
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun 	proc_fs_type = get_fs_type("proc");
699*4882a593Smuzhiyun 	if (!proc_fs_type)
700*4882a593Smuzhiyun 		return -ENODEV;
701*4882a593Smuzhiyun 
702*4882a593Smuzhiyun 	mnt = kern_mount(proc_fs_type);
703*4882a593Smuzhiyun 	put_filesystem(proc_fs_type);
704*4882a593Smuzhiyun 	if (IS_ERR(mnt))
705*4882a593Smuzhiyun 		return PTR_ERR(mnt);
706*4882a593Smuzhiyun 
707*4882a593Smuzhiyun 	proc_mnt = mnt;
708*4882a593Smuzhiyun 	return 0;
709*4882a593Smuzhiyun }
710*4882a593Smuzhiyun 
711*4882a593Smuzhiyun /*
712*4882a593Smuzhiyun  * Changed by mconsole_setup, which is __setup, and called before SMP is
713*4882a593Smuzhiyun  * active.
714*4882a593Smuzhiyun  */
715*4882a593Smuzhiyun static char *notify_socket = NULL;
716*4882a593Smuzhiyun 
mconsole_init(void)717*4882a593Smuzhiyun static int __init mconsole_init(void)
718*4882a593Smuzhiyun {
719*4882a593Smuzhiyun 	/* long to avoid size mismatch warnings from gcc */
720*4882a593Smuzhiyun 	long sock;
721*4882a593Smuzhiyun 	int err;
722*4882a593Smuzhiyun 	char file[UNIX_PATH_MAX];
723*4882a593Smuzhiyun 
724*4882a593Smuzhiyun 	mount_proc();
725*4882a593Smuzhiyun 
726*4882a593Smuzhiyun 	if (umid_file_name("mconsole", file, sizeof(file)))
727*4882a593Smuzhiyun 		return -1;
728*4882a593Smuzhiyun 	snprintf(mconsole_socket_name, sizeof(file), "%s", file);
729*4882a593Smuzhiyun 
730*4882a593Smuzhiyun 	sock = os_create_unix_socket(file, sizeof(file), 1);
731*4882a593Smuzhiyun 	if (sock < 0) {
732*4882a593Smuzhiyun 		printk(KERN_ERR "Failed to initialize management console\n");
733*4882a593Smuzhiyun 		return 1;
734*4882a593Smuzhiyun 	}
735*4882a593Smuzhiyun 	if (os_set_fd_block(sock, 0))
736*4882a593Smuzhiyun 		goto out;
737*4882a593Smuzhiyun 
738*4882a593Smuzhiyun 	register_reboot_notifier(&reboot_notifier);
739*4882a593Smuzhiyun 
740*4882a593Smuzhiyun 	err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt,
741*4882a593Smuzhiyun 			     IRQF_SHARED, "mconsole", (void *)sock);
742*4882a593Smuzhiyun 	if (err) {
743*4882a593Smuzhiyun 		printk(KERN_ERR "Failed to get IRQ for management console\n");
744*4882a593Smuzhiyun 		goto out;
745*4882a593Smuzhiyun 	}
746*4882a593Smuzhiyun 
747*4882a593Smuzhiyun 	if (notify_socket != NULL) {
748*4882a593Smuzhiyun 		notify_socket = kstrdup(notify_socket, GFP_KERNEL);
749*4882a593Smuzhiyun 		if (notify_socket != NULL)
750*4882a593Smuzhiyun 			mconsole_notify(notify_socket, MCONSOLE_SOCKET,
751*4882a593Smuzhiyun 					mconsole_socket_name,
752*4882a593Smuzhiyun 					strlen(mconsole_socket_name) + 1);
753*4882a593Smuzhiyun 		else printk(KERN_ERR "mconsole_setup failed to strdup "
754*4882a593Smuzhiyun 			    "string\n");
755*4882a593Smuzhiyun 	}
756*4882a593Smuzhiyun 
757*4882a593Smuzhiyun 	printk(KERN_INFO "mconsole (version %d) initialized on %s\n",
758*4882a593Smuzhiyun 	       MCONSOLE_VERSION, mconsole_socket_name);
759*4882a593Smuzhiyun 	return 0;
760*4882a593Smuzhiyun 
761*4882a593Smuzhiyun  out:
762*4882a593Smuzhiyun 	os_close_file(sock);
763*4882a593Smuzhiyun 	return 1;
764*4882a593Smuzhiyun }
765*4882a593Smuzhiyun 
766*4882a593Smuzhiyun __initcall(mconsole_init);
767*4882a593Smuzhiyun 
mconsole_proc_write(struct file * file,const char __user * buffer,size_t count,loff_t * pos)768*4882a593Smuzhiyun static ssize_t mconsole_proc_write(struct file *file,
769*4882a593Smuzhiyun 		const char __user *buffer, size_t count, loff_t *pos)
770*4882a593Smuzhiyun {
771*4882a593Smuzhiyun 	char *buf;
772*4882a593Smuzhiyun 
773*4882a593Smuzhiyun 	buf = memdup_user_nul(buffer, count);
774*4882a593Smuzhiyun 	if (IS_ERR(buf))
775*4882a593Smuzhiyun 		return PTR_ERR(buf);
776*4882a593Smuzhiyun 
777*4882a593Smuzhiyun 	mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count);
778*4882a593Smuzhiyun 	kfree(buf);
779*4882a593Smuzhiyun 	return count;
780*4882a593Smuzhiyun }
781*4882a593Smuzhiyun 
782*4882a593Smuzhiyun static const struct proc_ops mconsole_proc_ops = {
783*4882a593Smuzhiyun 	.proc_write	= mconsole_proc_write,
784*4882a593Smuzhiyun 	.proc_lseek	= noop_llseek,
785*4882a593Smuzhiyun };
786*4882a593Smuzhiyun 
create_proc_mconsole(void)787*4882a593Smuzhiyun static int create_proc_mconsole(void)
788*4882a593Smuzhiyun {
789*4882a593Smuzhiyun 	struct proc_dir_entry *ent;
790*4882a593Smuzhiyun 
791*4882a593Smuzhiyun 	if (notify_socket == NULL)
792*4882a593Smuzhiyun 		return 0;
793*4882a593Smuzhiyun 
794*4882a593Smuzhiyun 	ent = proc_create("mconsole", 0200, NULL, &mconsole_proc_ops);
795*4882a593Smuzhiyun 	if (ent == NULL) {
796*4882a593Smuzhiyun 		printk(KERN_INFO "create_proc_mconsole : proc_create failed\n");
797*4882a593Smuzhiyun 		return 0;
798*4882a593Smuzhiyun 	}
799*4882a593Smuzhiyun 	return 0;
800*4882a593Smuzhiyun }
801*4882a593Smuzhiyun 
802*4882a593Smuzhiyun static DEFINE_SPINLOCK(notify_spinlock);
803*4882a593Smuzhiyun 
lock_notify(void)804*4882a593Smuzhiyun void lock_notify(void)
805*4882a593Smuzhiyun {
806*4882a593Smuzhiyun 	spin_lock(&notify_spinlock);
807*4882a593Smuzhiyun }
808*4882a593Smuzhiyun 
unlock_notify(void)809*4882a593Smuzhiyun void unlock_notify(void)
810*4882a593Smuzhiyun {
811*4882a593Smuzhiyun 	spin_unlock(&notify_spinlock);
812*4882a593Smuzhiyun }
813*4882a593Smuzhiyun 
814*4882a593Smuzhiyun __initcall(create_proc_mconsole);
815*4882a593Smuzhiyun 
816*4882a593Smuzhiyun #define NOTIFY "notify:"
817*4882a593Smuzhiyun 
mconsole_setup(char * str)818*4882a593Smuzhiyun static int mconsole_setup(char *str)
819*4882a593Smuzhiyun {
820*4882a593Smuzhiyun 	if (!strncmp(str, NOTIFY, strlen(NOTIFY))) {
821*4882a593Smuzhiyun 		str += strlen(NOTIFY);
822*4882a593Smuzhiyun 		notify_socket = str;
823*4882a593Smuzhiyun 	}
824*4882a593Smuzhiyun 	else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str);
825*4882a593Smuzhiyun 	return 1;
826*4882a593Smuzhiyun }
827*4882a593Smuzhiyun 
828*4882a593Smuzhiyun __setup("mconsole=", mconsole_setup);
829*4882a593Smuzhiyun 
830*4882a593Smuzhiyun __uml_help(mconsole_setup,
831*4882a593Smuzhiyun "mconsole=notify:<socket>\n"
832*4882a593Smuzhiyun "    Requests that the mconsole driver send a message to the named Unix\n"
833*4882a593Smuzhiyun "    socket containing the name of the mconsole socket.  This also serves\n"
834*4882a593Smuzhiyun "    to notify outside processes when UML has booted far enough to respond\n"
835*4882a593Smuzhiyun "    to mconsole requests.\n\n"
836*4882a593Smuzhiyun );
837*4882a593Smuzhiyun 
notify_panic(struct notifier_block * self,unsigned long unused1,void * ptr)838*4882a593Smuzhiyun static int notify_panic(struct notifier_block *self, unsigned long unused1,
839*4882a593Smuzhiyun 			void *ptr)
840*4882a593Smuzhiyun {
841*4882a593Smuzhiyun 	char *message = ptr;
842*4882a593Smuzhiyun 
843*4882a593Smuzhiyun 	if (notify_socket == NULL)
844*4882a593Smuzhiyun 		return 0;
845*4882a593Smuzhiyun 
846*4882a593Smuzhiyun 	mconsole_notify(notify_socket, MCONSOLE_PANIC, message,
847*4882a593Smuzhiyun 			strlen(message) + 1);
848*4882a593Smuzhiyun 	return 0;
849*4882a593Smuzhiyun }
850*4882a593Smuzhiyun 
851*4882a593Smuzhiyun static struct notifier_block panic_exit_notifier = {
852*4882a593Smuzhiyun 	.notifier_call 		= notify_panic,
853*4882a593Smuzhiyun 	.next 			= NULL,
854*4882a593Smuzhiyun 	.priority 		= 1
855*4882a593Smuzhiyun };
856*4882a593Smuzhiyun 
add_notifier(void)857*4882a593Smuzhiyun static int add_notifier(void)
858*4882a593Smuzhiyun {
859*4882a593Smuzhiyun 	atomic_notifier_chain_register(&panic_notifier_list,
860*4882a593Smuzhiyun 			&panic_exit_notifier);
861*4882a593Smuzhiyun 	return 0;
862*4882a593Smuzhiyun }
863*4882a593Smuzhiyun 
864*4882a593Smuzhiyun __initcall(add_notifier);
865*4882a593Smuzhiyun 
mconsole_notify_socket(void)866*4882a593Smuzhiyun char *mconsole_notify_socket(void)
867*4882a593Smuzhiyun {
868*4882a593Smuzhiyun 	return notify_socket;
869*4882a593Smuzhiyun }
870*4882a593Smuzhiyun 
871*4882a593Smuzhiyun EXPORT_SYMBOL(mconsole_notify_socket);
872