LCOV - code coverage report
Current view: top level - arch/um/drivers - mconsole_kern.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 43 361 11.9 %
Date: 2023-08-24 13:40:31 Functions: 9 38 23.7 %

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

Generated by: LCOV version 1.14