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(¬ify_spinlock);
807*4882a593Smuzhiyun }
808*4882a593Smuzhiyun
unlock_notify(void)809*4882a593Smuzhiyun void unlock_notify(void)
810*4882a593Smuzhiyun {
811*4882a593Smuzhiyun spin_unlock(¬ify_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