18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2001 Lennert Buytenhek (buytenh@gnu.org) 48c2ecf20Sopenharmony_ci * Copyright (C) 2001 - 2008 Jeff Dike (jdike@{addtoit,linux.intel}.com) 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/console.h> 88c2ecf20Sopenharmony_ci#include <linux/ctype.h> 98c2ecf20Sopenharmony_ci#include <linux/string.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/list.h> 128c2ecf20Sopenharmony_ci#include <linux/mm.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/notifier.h> 158c2ecf20Sopenharmony_ci#include <linux/reboot.h> 168c2ecf20Sopenharmony_ci#include <linux/sched/debug.h> 178c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/syscalls.h> 208c2ecf20Sopenharmony_ci#include <linux/utsname.h> 218c2ecf20Sopenharmony_ci#include <linux/socket.h> 228c2ecf20Sopenharmony_ci#include <linux/un.h> 238c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 248c2ecf20Sopenharmony_ci#include <linux/mutex.h> 258c2ecf20Sopenharmony_ci#include <linux/fs.h> 268c2ecf20Sopenharmony_ci#include <linux/mount.h> 278c2ecf20Sopenharmony_ci#include <linux/file.h> 288c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 298c2ecf20Sopenharmony_ci#include <asm/switch_to.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <init.h> 328c2ecf20Sopenharmony_ci#include <irq_kern.h> 338c2ecf20Sopenharmony_ci#include <irq_user.h> 348c2ecf20Sopenharmony_ci#include <kern_util.h> 358c2ecf20Sopenharmony_ci#include "mconsole.h" 368c2ecf20Sopenharmony_ci#include "mconsole_kern.h" 378c2ecf20Sopenharmony_ci#include <os.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic struct vfsmount *proc_mnt = NULL; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int do_unlink_socket(struct notifier_block *notifier, 428c2ecf20Sopenharmony_ci unsigned long what, void *data) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci return mconsole_unlink_socket(); 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic struct notifier_block reboot_notifier = { 498c2ecf20Sopenharmony_ci .notifier_call = do_unlink_socket, 508c2ecf20Sopenharmony_ci .priority = 0, 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* Safe without explicit locking for now. Tasklets provide their own 548c2ecf20Sopenharmony_ci * locking, and the interrupt handler is safe because it can't interrupt 558c2ecf20Sopenharmony_ci * itself and it can only happen on CPU 0. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic LIST_HEAD(mc_requests); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void mc_work_proc(struct work_struct *unused) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct mconsole_entry *req; 638c2ecf20Sopenharmony_ci unsigned long flags; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci while (!list_empty(&mc_requests)) { 668c2ecf20Sopenharmony_ci local_irq_save(flags); 678c2ecf20Sopenharmony_ci req = list_entry(mc_requests.next, struct mconsole_entry, list); 688c2ecf20Sopenharmony_ci list_del(&req->list); 698c2ecf20Sopenharmony_ci local_irq_restore(flags); 708c2ecf20Sopenharmony_ci req->request.cmd->handler(&req->request); 718c2ecf20Sopenharmony_ci kfree(req); 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic DECLARE_WORK(mconsole_work, mc_work_proc); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic irqreturn_t mconsole_interrupt(int irq, void *dev_id) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci /* long to avoid size mismatch warnings from gcc */ 808c2ecf20Sopenharmony_ci long fd; 818c2ecf20Sopenharmony_ci struct mconsole_entry *new; 828c2ecf20Sopenharmony_ci static struct mc_request req; /* that's OK */ 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci fd = (long) dev_id; 858c2ecf20Sopenharmony_ci while (mconsole_get_request(fd, &req)) { 868c2ecf20Sopenharmony_ci if (req.cmd->context == MCONSOLE_INTR) 878c2ecf20Sopenharmony_ci (*req.cmd->handler)(&req); 888c2ecf20Sopenharmony_ci else { 898c2ecf20Sopenharmony_ci new = kmalloc(sizeof(*new), GFP_NOWAIT); 908c2ecf20Sopenharmony_ci if (new == NULL) 918c2ecf20Sopenharmony_ci mconsole_reply(&req, "Out of memory", 1, 0); 928c2ecf20Sopenharmony_ci else { 938c2ecf20Sopenharmony_ci new->request = req; 948c2ecf20Sopenharmony_ci new->request.regs = get_irq_regs()->regs; 958c2ecf20Sopenharmony_ci list_add(&new->list, &mc_requests); 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci if (!list_empty(&mc_requests)) 1008c2ecf20Sopenharmony_ci schedule_work(&mconsole_work); 1018c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_civoid mconsole_version(struct mc_request *req) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci char version[256]; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci sprintf(version, "%s %s %s %s %s", utsname()->sysname, 1098c2ecf20Sopenharmony_ci utsname()->nodename, utsname()->release, utsname()->version, 1108c2ecf20Sopenharmony_ci utsname()->machine); 1118c2ecf20Sopenharmony_ci mconsole_reply(req, version, 0, 0); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_civoid mconsole_log(struct mc_request *req) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci int len; 1178c2ecf20Sopenharmony_ci char *ptr = req->request.data; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci ptr += strlen("log "); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci len = req->len - (ptr - req->request.data); 1228c2ecf20Sopenharmony_ci printk(KERN_WARNING "%.*s", len, ptr); 1238c2ecf20Sopenharmony_ci mconsole_reply(req, "", 0, 0); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_civoid mconsole_proc(struct mc_request *req) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci struct vfsmount *mnt = proc_mnt; 1298c2ecf20Sopenharmony_ci char *buf; 1308c2ecf20Sopenharmony_ci int len; 1318c2ecf20Sopenharmony_ci struct file *file; 1328c2ecf20Sopenharmony_ci int first_chunk = 1; 1338c2ecf20Sopenharmony_ci char *ptr = req->request.data; 1348c2ecf20Sopenharmony_ci loff_t pos = 0; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci ptr += strlen("proc"); 1378c2ecf20Sopenharmony_ci ptr = skip_spaces(ptr); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (!mnt) { 1408c2ecf20Sopenharmony_ci mconsole_reply(req, "Proc not available", 1, 0); 1418c2ecf20Sopenharmony_ci goto out; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci file = file_open_root_mnt(mnt, ptr, O_RDONLY, 0); 1448c2ecf20Sopenharmony_ci if (IS_ERR(file)) { 1458c2ecf20Sopenharmony_ci mconsole_reply(req, "Failed to open file", 1, 0); 1468c2ecf20Sopenharmony_ci printk(KERN_ERR "open /proc/%s: %ld\n", ptr, PTR_ERR(file)); 1478c2ecf20Sopenharmony_ci goto out; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 1518c2ecf20Sopenharmony_ci if (buf == NULL) { 1528c2ecf20Sopenharmony_ci mconsole_reply(req, "Failed to allocate buffer", 1, 0); 1538c2ecf20Sopenharmony_ci goto out_fput; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci do { 1578c2ecf20Sopenharmony_ci len = kernel_read(file, buf, PAGE_SIZE - 1, &pos); 1588c2ecf20Sopenharmony_ci if (len < 0) { 1598c2ecf20Sopenharmony_ci mconsole_reply(req, "Read of file failed", 1, 0); 1608c2ecf20Sopenharmony_ci goto out_free; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci /* Begin the file content on his own line. */ 1638c2ecf20Sopenharmony_ci if (first_chunk) { 1648c2ecf20Sopenharmony_ci mconsole_reply(req, "\n", 0, 1); 1658c2ecf20Sopenharmony_ci first_chunk = 0; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci buf[len] = '\0'; 1688c2ecf20Sopenharmony_ci mconsole_reply(req, buf, 0, (len > 0)); 1698c2ecf20Sopenharmony_ci } while (len > 0); 1708c2ecf20Sopenharmony_ci out_free: 1718c2ecf20Sopenharmony_ci kfree(buf); 1728c2ecf20Sopenharmony_ci out_fput: 1738c2ecf20Sopenharmony_ci fput(file); 1748c2ecf20Sopenharmony_ci out: ; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci#define UML_MCONSOLE_HELPTEXT \ 1788c2ecf20Sopenharmony_ci"Commands: \n\ 1798c2ecf20Sopenharmony_ci version - Get kernel version \n\ 1808c2ecf20Sopenharmony_ci help - Print this message \n\ 1818c2ecf20Sopenharmony_ci halt - Halt UML \n\ 1828c2ecf20Sopenharmony_ci reboot - Reboot UML \n\ 1838c2ecf20Sopenharmony_ci config <dev>=<config> - Add a new device to UML; \n\ 1848c2ecf20Sopenharmony_ci same syntax as command line \n\ 1858c2ecf20Sopenharmony_ci config <dev> - Query the configuration of a device \n\ 1868c2ecf20Sopenharmony_ci remove <dev> - Remove a device from UML \n\ 1878c2ecf20Sopenharmony_ci sysrq <letter> - Performs the SysRq action controlled by the letter \n\ 1888c2ecf20Sopenharmony_ci cad - invoke the Ctrl-Alt-Del handler \n\ 1898c2ecf20Sopenharmony_ci stop - pause the UML; it will do nothing until it receives a 'go' \n\ 1908c2ecf20Sopenharmony_ci go - continue the UML after a 'stop' \n\ 1918c2ecf20Sopenharmony_ci log <string> - make UML enter <string> into the kernel log\n\ 1928c2ecf20Sopenharmony_ci proc <file> - returns the contents of the UML's /proc/<file>\n\ 1938c2ecf20Sopenharmony_ci stack <pid> - returns the stack of the specified pid\n\ 1948c2ecf20Sopenharmony_ci" 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_civoid mconsole_help(struct mc_request *req) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci mconsole_reply(req, UML_MCONSOLE_HELPTEXT, 0, 0); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_civoid mconsole_halt(struct mc_request *req) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci mconsole_reply(req, "", 0, 0); 2048c2ecf20Sopenharmony_ci machine_halt(); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_civoid mconsole_reboot(struct mc_request *req) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci mconsole_reply(req, "", 0, 0); 2108c2ecf20Sopenharmony_ci machine_restart(NULL); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_civoid mconsole_cad(struct mc_request *req) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci mconsole_reply(req, "", 0, 0); 2168c2ecf20Sopenharmony_ci ctrl_alt_del(); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_civoid mconsole_go(struct mc_request *req) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci mconsole_reply(req, "Not stopped", 1, 0); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_civoid mconsole_stop(struct mc_request *req) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci block_signals(); 2278c2ecf20Sopenharmony_ci os_set_fd_block(req->originating_fd, 1); 2288c2ecf20Sopenharmony_ci mconsole_reply(req, "stopped", 0, 0); 2298c2ecf20Sopenharmony_ci for (;;) { 2308c2ecf20Sopenharmony_ci if (!mconsole_get_request(req->originating_fd, req)) 2318c2ecf20Sopenharmony_ci continue; 2328c2ecf20Sopenharmony_ci if (req->cmd->handler == mconsole_go) 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci if (req->cmd->handler == mconsole_stop) { 2358c2ecf20Sopenharmony_ci mconsole_reply(req, "Already stopped", 1, 0); 2368c2ecf20Sopenharmony_ci continue; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci if (req->cmd->handler == mconsole_sysrq) { 2398c2ecf20Sopenharmony_ci struct pt_regs *old_regs; 2408c2ecf20Sopenharmony_ci old_regs = set_irq_regs((struct pt_regs *)&req->regs); 2418c2ecf20Sopenharmony_ci mconsole_sysrq(req); 2428c2ecf20Sopenharmony_ci set_irq_regs(old_regs); 2438c2ecf20Sopenharmony_ci continue; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci (*req->cmd->handler)(req); 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci os_set_fd_block(req->originating_fd, 0); 2488c2ecf20Sopenharmony_ci mconsole_reply(req, "", 0, 0); 2498c2ecf20Sopenharmony_ci unblock_signals(); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(mc_devices_lock); 2538c2ecf20Sopenharmony_cistatic LIST_HEAD(mconsole_devices); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_civoid mconsole_register_dev(struct mc_device *new) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci spin_lock(&mc_devices_lock); 2588c2ecf20Sopenharmony_ci BUG_ON(!list_empty(&new->list)); 2598c2ecf20Sopenharmony_ci list_add(&new->list, &mconsole_devices); 2608c2ecf20Sopenharmony_ci spin_unlock(&mc_devices_lock); 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic struct mc_device *mconsole_find_dev(char *name) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci struct list_head *ele; 2668c2ecf20Sopenharmony_ci struct mc_device *dev; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci list_for_each(ele, &mconsole_devices) { 2698c2ecf20Sopenharmony_ci dev = list_entry(ele, struct mc_device, list); 2708c2ecf20Sopenharmony_ci if (!strncmp(name, dev->name, strlen(dev->name))) 2718c2ecf20Sopenharmony_ci return dev; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci return NULL; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci#define UNPLUGGED_PER_PAGE \ 2778c2ecf20Sopenharmony_ci ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long)) 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistruct unplugged_pages { 2808c2ecf20Sopenharmony_ci struct list_head list; 2818c2ecf20Sopenharmony_ci void *pages[UNPLUGGED_PER_PAGE]; 2828c2ecf20Sopenharmony_ci}; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(plug_mem_mutex); 2858c2ecf20Sopenharmony_cistatic unsigned long long unplugged_pages_count = 0; 2868c2ecf20Sopenharmony_cistatic LIST_HEAD(unplugged_pages); 2878c2ecf20Sopenharmony_cistatic int unplug_index = UNPLUGGED_PER_PAGE; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic int mem_config(char *str, char **error_out) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci unsigned long long diff; 2928c2ecf20Sopenharmony_ci int err = -EINVAL, i, add; 2938c2ecf20Sopenharmony_ci char *ret; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci if (str[0] != '=') { 2968c2ecf20Sopenharmony_ci *error_out = "Expected '=' after 'mem'"; 2978c2ecf20Sopenharmony_ci goto out; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci str++; 3018c2ecf20Sopenharmony_ci if (str[0] == '-') 3028c2ecf20Sopenharmony_ci add = 0; 3038c2ecf20Sopenharmony_ci else if (str[0] == '+') { 3048c2ecf20Sopenharmony_ci add = 1; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci else { 3078c2ecf20Sopenharmony_ci *error_out = "Expected increment to start with '-' or '+'"; 3088c2ecf20Sopenharmony_ci goto out; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci str++; 3128c2ecf20Sopenharmony_ci diff = memparse(str, &ret); 3138c2ecf20Sopenharmony_ci if (*ret != '\0') { 3148c2ecf20Sopenharmony_ci *error_out = "Failed to parse memory increment"; 3158c2ecf20Sopenharmony_ci goto out; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci diff /= PAGE_SIZE; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci mutex_lock(&plug_mem_mutex); 3218c2ecf20Sopenharmony_ci for (i = 0; i < diff; i++) { 3228c2ecf20Sopenharmony_ci struct unplugged_pages *unplugged; 3238c2ecf20Sopenharmony_ci void *addr; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (add) { 3268c2ecf20Sopenharmony_ci if (list_empty(&unplugged_pages)) 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci unplugged = list_entry(unplugged_pages.next, 3308c2ecf20Sopenharmony_ci struct unplugged_pages, list); 3318c2ecf20Sopenharmony_ci if (unplug_index > 0) 3328c2ecf20Sopenharmony_ci addr = unplugged->pages[--unplug_index]; 3338c2ecf20Sopenharmony_ci else { 3348c2ecf20Sopenharmony_ci list_del(&unplugged->list); 3358c2ecf20Sopenharmony_ci addr = unplugged; 3368c2ecf20Sopenharmony_ci unplug_index = UNPLUGGED_PER_PAGE; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci free_page((unsigned long) addr); 3408c2ecf20Sopenharmony_ci unplugged_pages_count--; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci else { 3438c2ecf20Sopenharmony_ci struct page *page; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci page = alloc_page(GFP_ATOMIC); 3468c2ecf20Sopenharmony_ci if (page == NULL) 3478c2ecf20Sopenharmony_ci break; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci unplugged = page_address(page); 3508c2ecf20Sopenharmony_ci if (unplug_index == UNPLUGGED_PER_PAGE) { 3518c2ecf20Sopenharmony_ci list_add(&unplugged->list, &unplugged_pages); 3528c2ecf20Sopenharmony_ci unplug_index = 0; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci else { 3558c2ecf20Sopenharmony_ci struct list_head *entry = unplugged_pages.next; 3568c2ecf20Sopenharmony_ci addr = unplugged; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci unplugged = list_entry(entry, 3598c2ecf20Sopenharmony_ci struct unplugged_pages, 3608c2ecf20Sopenharmony_ci list); 3618c2ecf20Sopenharmony_ci err = os_drop_memory(addr, PAGE_SIZE); 3628c2ecf20Sopenharmony_ci if (err) { 3638c2ecf20Sopenharmony_ci printk(KERN_ERR "Failed to release " 3648c2ecf20Sopenharmony_ci "memory - errno = %d\n", err); 3658c2ecf20Sopenharmony_ci *error_out = "Failed to release memory"; 3668c2ecf20Sopenharmony_ci goto out_unlock; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci unplugged->pages[unplug_index++] = addr; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci unplugged_pages_count++; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci err = 0; 3768c2ecf20Sopenharmony_ciout_unlock: 3778c2ecf20Sopenharmony_ci mutex_unlock(&plug_mem_mutex); 3788c2ecf20Sopenharmony_ciout: 3798c2ecf20Sopenharmony_ci return err; 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic int mem_get_config(char *name, char *str, int size, char **error_out) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci char buf[sizeof("18446744073709551615")]; 3858c2ecf20Sopenharmony_ci int len = 0; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci sprintf(buf, "%ld", uml_physmem); 3888c2ecf20Sopenharmony_ci CONFIG_CHUNK(str, size, len, buf, 1); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci return len; 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic int mem_id(char **str, int *start_out, int *end_out) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci *start_out = 0; 3968c2ecf20Sopenharmony_ci *end_out = 0; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int mem_remove(int n, char **error_out) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci *error_out = "Memory doesn't support the remove operation"; 4048c2ecf20Sopenharmony_ci return -EBUSY; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic struct mc_device mem_mc = { 4088c2ecf20Sopenharmony_ci .list = LIST_HEAD_INIT(mem_mc.list), 4098c2ecf20Sopenharmony_ci .name = "mem", 4108c2ecf20Sopenharmony_ci .config = mem_config, 4118c2ecf20Sopenharmony_ci .get_config = mem_get_config, 4128c2ecf20Sopenharmony_ci .id = mem_id, 4138c2ecf20Sopenharmony_ci .remove = mem_remove, 4148c2ecf20Sopenharmony_ci}; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic int __init mem_mc_init(void) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci if (can_drop_memory()) 4198c2ecf20Sopenharmony_ci mconsole_register_dev(&mem_mc); 4208c2ecf20Sopenharmony_ci else printk(KERN_ERR "Can't release memory to the host - memory " 4218c2ecf20Sopenharmony_ci "hotplug won't be supported\n"); 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci__initcall(mem_mc_init); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci#define CONFIG_BUF_SIZE 64 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic void mconsole_get_config(int (*get_config)(char *, char *, int, 4308c2ecf20Sopenharmony_ci char **), 4318c2ecf20Sopenharmony_ci struct mc_request *req, char *name) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci char default_buf[CONFIG_BUF_SIZE], *error, *buf; 4348c2ecf20Sopenharmony_ci int n, size; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (get_config == NULL) { 4378c2ecf20Sopenharmony_ci mconsole_reply(req, "No get_config routine defined", 1, 0); 4388c2ecf20Sopenharmony_ci return; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci error = NULL; 4428c2ecf20Sopenharmony_ci size = ARRAY_SIZE(default_buf); 4438c2ecf20Sopenharmony_ci buf = default_buf; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci while (1) { 4468c2ecf20Sopenharmony_ci n = (*get_config)(name, buf, size, &error); 4478c2ecf20Sopenharmony_ci if (error != NULL) { 4488c2ecf20Sopenharmony_ci mconsole_reply(req, error, 1, 0); 4498c2ecf20Sopenharmony_ci goto out; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (n <= size) { 4538c2ecf20Sopenharmony_ci mconsole_reply(req, buf, 0, 0); 4548c2ecf20Sopenharmony_ci goto out; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (buf != default_buf) 4588c2ecf20Sopenharmony_ci kfree(buf); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci size = n; 4618c2ecf20Sopenharmony_ci buf = kmalloc(size, GFP_KERNEL); 4628c2ecf20Sopenharmony_ci if (buf == NULL) { 4638c2ecf20Sopenharmony_ci mconsole_reply(req, "Failed to allocate buffer", 1, 0); 4648c2ecf20Sopenharmony_ci return; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci out: 4688c2ecf20Sopenharmony_ci if (buf != default_buf) 4698c2ecf20Sopenharmony_ci kfree(buf); 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_civoid mconsole_config(struct mc_request *req) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct mc_device *dev; 4758c2ecf20Sopenharmony_ci char *ptr = req->request.data, *name, *error_string = ""; 4768c2ecf20Sopenharmony_ci int err; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci ptr += strlen("config"); 4798c2ecf20Sopenharmony_ci ptr = skip_spaces(ptr); 4808c2ecf20Sopenharmony_ci dev = mconsole_find_dev(ptr); 4818c2ecf20Sopenharmony_ci if (dev == NULL) { 4828c2ecf20Sopenharmony_ci mconsole_reply(req, "Bad configuration option", 1, 0); 4838c2ecf20Sopenharmony_ci return; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci name = &ptr[strlen(dev->name)]; 4878c2ecf20Sopenharmony_ci ptr = name; 4888c2ecf20Sopenharmony_ci while ((*ptr != '=') && (*ptr != '\0')) 4898c2ecf20Sopenharmony_ci ptr++; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (*ptr == '=') { 4928c2ecf20Sopenharmony_ci err = (*dev->config)(name, &error_string); 4938c2ecf20Sopenharmony_ci mconsole_reply(req, error_string, err, 0); 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci else mconsole_get_config(dev->get_config, req, name); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_civoid mconsole_remove(struct mc_request *req) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct mc_device *dev; 5018c2ecf20Sopenharmony_ci char *ptr = req->request.data, *err_msg = ""; 5028c2ecf20Sopenharmony_ci char error[256]; 5038c2ecf20Sopenharmony_ci int err, start, end, n; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci ptr += strlen("remove"); 5068c2ecf20Sopenharmony_ci ptr = skip_spaces(ptr); 5078c2ecf20Sopenharmony_ci dev = mconsole_find_dev(ptr); 5088c2ecf20Sopenharmony_ci if (dev == NULL) { 5098c2ecf20Sopenharmony_ci mconsole_reply(req, "Bad remove option", 1, 0); 5108c2ecf20Sopenharmony_ci return; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci ptr = &ptr[strlen(dev->name)]; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci err = 1; 5168c2ecf20Sopenharmony_ci n = (*dev->id)(&ptr, &start, &end); 5178c2ecf20Sopenharmony_ci if (n < 0) { 5188c2ecf20Sopenharmony_ci err_msg = "Couldn't parse device number"; 5198c2ecf20Sopenharmony_ci goto out; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci else if ((n < start) || (n > end)) { 5228c2ecf20Sopenharmony_ci sprintf(error, "Invalid device number - must be between " 5238c2ecf20Sopenharmony_ci "%d and %d", start, end); 5248c2ecf20Sopenharmony_ci err_msg = error; 5258c2ecf20Sopenharmony_ci goto out; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci err_msg = NULL; 5298c2ecf20Sopenharmony_ci err = (*dev->remove)(n, &err_msg); 5308c2ecf20Sopenharmony_ci switch(err) { 5318c2ecf20Sopenharmony_ci case 0: 5328c2ecf20Sopenharmony_ci err_msg = ""; 5338c2ecf20Sopenharmony_ci break; 5348c2ecf20Sopenharmony_ci case -ENODEV: 5358c2ecf20Sopenharmony_ci if (err_msg == NULL) 5368c2ecf20Sopenharmony_ci err_msg = "Device doesn't exist"; 5378c2ecf20Sopenharmony_ci break; 5388c2ecf20Sopenharmony_ci case -EBUSY: 5398c2ecf20Sopenharmony_ci if (err_msg == NULL) 5408c2ecf20Sopenharmony_ci err_msg = "Device is currently open"; 5418c2ecf20Sopenharmony_ci break; 5428c2ecf20Sopenharmony_ci default: 5438c2ecf20Sopenharmony_ci break; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ciout: 5468c2ecf20Sopenharmony_ci mconsole_reply(req, err_msg, err, 0); 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistruct mconsole_output { 5508c2ecf20Sopenharmony_ci struct list_head list; 5518c2ecf20Sopenharmony_ci struct mc_request *req; 5528c2ecf20Sopenharmony_ci}; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(client_lock); 5558c2ecf20Sopenharmony_cistatic LIST_HEAD(clients); 5568c2ecf20Sopenharmony_cistatic char console_buf[MCONSOLE_MAX_DATA]; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cistatic void console_write(struct console *console, const char *string, 5598c2ecf20Sopenharmony_ci unsigned int len) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci struct list_head *ele; 5628c2ecf20Sopenharmony_ci int n; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (list_empty(&clients)) 5658c2ecf20Sopenharmony_ci return; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci while (len > 0) { 5688c2ecf20Sopenharmony_ci n = min((size_t) len, ARRAY_SIZE(console_buf)); 5698c2ecf20Sopenharmony_ci strncpy(console_buf, string, n); 5708c2ecf20Sopenharmony_ci string += n; 5718c2ecf20Sopenharmony_ci len -= n; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci list_for_each(ele, &clients) { 5748c2ecf20Sopenharmony_ci struct mconsole_output *entry; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci entry = list_entry(ele, struct mconsole_output, list); 5778c2ecf20Sopenharmony_ci mconsole_reply_len(entry->req, console_buf, n, 0, 1); 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_cistatic struct console mc_console = { .name = "mc", 5838c2ecf20Sopenharmony_ci .write = console_write, 5848c2ecf20Sopenharmony_ci .flags = CON_ENABLED, 5858c2ecf20Sopenharmony_ci .index = -1 }; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic int mc_add_console(void) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci register_console(&mc_console); 5908c2ecf20Sopenharmony_ci return 0; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cilate_initcall(mc_add_console); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic void with_console(struct mc_request *req, void (*proc)(void *), 5968c2ecf20Sopenharmony_ci void *arg) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci struct mconsole_output entry; 5998c2ecf20Sopenharmony_ci unsigned long flags; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci entry.req = req; 6028c2ecf20Sopenharmony_ci spin_lock_irqsave(&client_lock, flags); 6038c2ecf20Sopenharmony_ci list_add(&entry.list, &clients); 6048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&client_lock, flags); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci (*proc)(arg); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci mconsole_reply_len(req, "", 0, 0, 0); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci spin_lock_irqsave(&client_lock, flags); 6118c2ecf20Sopenharmony_ci list_del(&entry.list); 6128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&client_lock, flags); 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci#ifdef CONFIG_MAGIC_SYSRQ 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci#include <linux/sysrq.h> 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic void sysrq_proc(void *arg) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci char *op = arg; 6228c2ecf20Sopenharmony_ci handle_sysrq(*op); 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_civoid mconsole_sysrq(struct mc_request *req) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci char *ptr = req->request.data; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci ptr += strlen("sysrq"); 6308c2ecf20Sopenharmony_ci ptr = skip_spaces(ptr); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci /* 6338c2ecf20Sopenharmony_ci * With 'b', the system will shut down without a chance to reply, 6348c2ecf20Sopenharmony_ci * so in this case, we reply first. 6358c2ecf20Sopenharmony_ci */ 6368c2ecf20Sopenharmony_ci if (*ptr == 'b') 6378c2ecf20Sopenharmony_ci mconsole_reply(req, "", 0, 0); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci with_console(req, sysrq_proc, ptr); 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci#else 6428c2ecf20Sopenharmony_civoid mconsole_sysrq(struct mc_request *req) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci mconsole_reply(req, "Sysrq not compiled in", 1, 0); 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci#endif 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic void stack_proc(void *arg) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci struct task_struct *task = arg; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci show_stack(task, NULL, KERN_INFO); 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci/* 6568c2ecf20Sopenharmony_ci * Mconsole stack trace 6578c2ecf20Sopenharmony_ci * Added by Allan Graves, Jeff Dike 6588c2ecf20Sopenharmony_ci * Dumps a stacks registers to the linux console. 6598c2ecf20Sopenharmony_ci * Usage stack <pid>. 6608c2ecf20Sopenharmony_ci */ 6618c2ecf20Sopenharmony_civoid mconsole_stack(struct mc_request *req) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci char *ptr = req->request.data; 6648c2ecf20Sopenharmony_ci int pid_requested= -1; 6658c2ecf20Sopenharmony_ci struct task_struct *to = NULL; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci /* 6688c2ecf20Sopenharmony_ci * Would be nice: 6698c2ecf20Sopenharmony_ci * 1) Send showregs output to mconsole. 6708c2ecf20Sopenharmony_ci * 2) Add a way to stack dump all pids. 6718c2ecf20Sopenharmony_ci */ 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci ptr += strlen("stack"); 6748c2ecf20Sopenharmony_ci ptr = skip_spaces(ptr); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci /* 6778c2ecf20Sopenharmony_ci * Should really check for multiple pids or reject bad args here 6788c2ecf20Sopenharmony_ci */ 6798c2ecf20Sopenharmony_ci /* What do the arguments in mconsole_reply mean? */ 6808c2ecf20Sopenharmony_ci if (sscanf(ptr, "%d", &pid_requested) == 0) { 6818c2ecf20Sopenharmony_ci mconsole_reply(req, "Please specify a pid", 1, 0); 6828c2ecf20Sopenharmony_ci return; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci to = find_task_by_pid_ns(pid_requested, &init_pid_ns); 6868c2ecf20Sopenharmony_ci if ((to == NULL) || (pid_requested == 0)) { 6878c2ecf20Sopenharmony_ci mconsole_reply(req, "Couldn't find that pid", 1, 0); 6888c2ecf20Sopenharmony_ci return; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci with_console(req, stack_proc, to); 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic int __init mount_proc(void) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci struct file_system_type *proc_fs_type; 6968c2ecf20Sopenharmony_ci struct vfsmount *mnt; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci proc_fs_type = get_fs_type("proc"); 6998c2ecf20Sopenharmony_ci if (!proc_fs_type) 7008c2ecf20Sopenharmony_ci return -ENODEV; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci mnt = kern_mount(proc_fs_type); 7038c2ecf20Sopenharmony_ci put_filesystem(proc_fs_type); 7048c2ecf20Sopenharmony_ci if (IS_ERR(mnt)) 7058c2ecf20Sopenharmony_ci return PTR_ERR(mnt); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci proc_mnt = mnt; 7088c2ecf20Sopenharmony_ci return 0; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci/* 7128c2ecf20Sopenharmony_ci * Changed by mconsole_setup, which is __setup, and called before SMP is 7138c2ecf20Sopenharmony_ci * active. 7148c2ecf20Sopenharmony_ci */ 7158c2ecf20Sopenharmony_cistatic char *notify_socket = NULL; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_cistatic int __init mconsole_init(void) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci /* long to avoid size mismatch warnings from gcc */ 7208c2ecf20Sopenharmony_ci long sock; 7218c2ecf20Sopenharmony_ci int err; 7228c2ecf20Sopenharmony_ci char file[UNIX_PATH_MAX]; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci mount_proc(); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci if (umid_file_name("mconsole", file, sizeof(file))) 7278c2ecf20Sopenharmony_ci return -1; 7288c2ecf20Sopenharmony_ci snprintf(mconsole_socket_name, sizeof(file), "%s", file); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci sock = os_create_unix_socket(file, sizeof(file), 1); 7318c2ecf20Sopenharmony_ci if (sock < 0) { 7328c2ecf20Sopenharmony_ci printk(KERN_ERR "Failed to initialize management console\n"); 7338c2ecf20Sopenharmony_ci return 1; 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci if (os_set_fd_block(sock, 0)) 7368c2ecf20Sopenharmony_ci goto out; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci register_reboot_notifier(&reboot_notifier); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci err = um_request_irq(MCONSOLE_IRQ, sock, IRQ_READ, mconsole_interrupt, 7418c2ecf20Sopenharmony_ci IRQF_SHARED, "mconsole", (void *)sock); 7428c2ecf20Sopenharmony_ci if (err) { 7438c2ecf20Sopenharmony_ci printk(KERN_ERR "Failed to get IRQ for management console\n"); 7448c2ecf20Sopenharmony_ci goto out; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (notify_socket != NULL) { 7488c2ecf20Sopenharmony_ci notify_socket = kstrdup(notify_socket, GFP_KERNEL); 7498c2ecf20Sopenharmony_ci if (notify_socket != NULL) 7508c2ecf20Sopenharmony_ci mconsole_notify(notify_socket, MCONSOLE_SOCKET, 7518c2ecf20Sopenharmony_ci mconsole_socket_name, 7528c2ecf20Sopenharmony_ci strlen(mconsole_socket_name) + 1); 7538c2ecf20Sopenharmony_ci else printk(KERN_ERR "mconsole_setup failed to strdup " 7548c2ecf20Sopenharmony_ci "string\n"); 7558c2ecf20Sopenharmony_ci } 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci printk(KERN_INFO "mconsole (version %d) initialized on %s\n", 7588c2ecf20Sopenharmony_ci MCONSOLE_VERSION, mconsole_socket_name); 7598c2ecf20Sopenharmony_ci return 0; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci out: 7628c2ecf20Sopenharmony_ci os_close_file(sock); 7638c2ecf20Sopenharmony_ci return 1; 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci__initcall(mconsole_init); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_cistatic ssize_t mconsole_proc_write(struct file *file, 7698c2ecf20Sopenharmony_ci const char __user *buffer, size_t count, loff_t *pos) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci char *buf; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci buf = memdup_user_nul(buffer, count); 7748c2ecf20Sopenharmony_ci if (IS_ERR(buf)) 7758c2ecf20Sopenharmony_ci return PTR_ERR(buf); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci mconsole_notify(notify_socket, MCONSOLE_USER_NOTIFY, buf, count); 7788c2ecf20Sopenharmony_ci kfree(buf); 7798c2ecf20Sopenharmony_ci return count; 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_cistatic const struct proc_ops mconsole_proc_ops = { 7838c2ecf20Sopenharmony_ci .proc_write = mconsole_proc_write, 7848c2ecf20Sopenharmony_ci .proc_lseek = noop_llseek, 7858c2ecf20Sopenharmony_ci}; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_cistatic int create_proc_mconsole(void) 7888c2ecf20Sopenharmony_ci{ 7898c2ecf20Sopenharmony_ci struct proc_dir_entry *ent; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci if (notify_socket == NULL) 7928c2ecf20Sopenharmony_ci return 0; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci ent = proc_create("mconsole", 0200, NULL, &mconsole_proc_ops); 7958c2ecf20Sopenharmony_ci if (ent == NULL) { 7968c2ecf20Sopenharmony_ci printk(KERN_INFO "create_proc_mconsole : proc_create failed\n"); 7978c2ecf20Sopenharmony_ci return 0; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci return 0; 8008c2ecf20Sopenharmony_ci} 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(notify_spinlock); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_civoid lock_notify(void) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci spin_lock(¬ify_spinlock); 8078c2ecf20Sopenharmony_ci} 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_civoid unlock_notify(void) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci spin_unlock(¬ify_spinlock); 8128c2ecf20Sopenharmony_ci} 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci__initcall(create_proc_mconsole); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci#define NOTIFY "notify:" 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_cistatic int mconsole_setup(char *str) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci if (!strncmp(str, NOTIFY, strlen(NOTIFY))) { 8218c2ecf20Sopenharmony_ci str += strlen(NOTIFY); 8228c2ecf20Sopenharmony_ci notify_socket = str; 8238c2ecf20Sopenharmony_ci } 8248c2ecf20Sopenharmony_ci else printk(KERN_ERR "mconsole_setup : Unknown option - '%s'\n", str); 8258c2ecf20Sopenharmony_ci return 1; 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci__setup("mconsole=", mconsole_setup); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci__uml_help(mconsole_setup, 8318c2ecf20Sopenharmony_ci"mconsole=notify:<socket>\n" 8328c2ecf20Sopenharmony_ci" Requests that the mconsole driver send a message to the named Unix\n" 8338c2ecf20Sopenharmony_ci" socket containing the name of the mconsole socket. This also serves\n" 8348c2ecf20Sopenharmony_ci" to notify outside processes when UML has booted far enough to respond\n" 8358c2ecf20Sopenharmony_ci" to mconsole requests.\n\n" 8368c2ecf20Sopenharmony_ci); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_cistatic int notify_panic(struct notifier_block *self, unsigned long unused1, 8398c2ecf20Sopenharmony_ci void *ptr) 8408c2ecf20Sopenharmony_ci{ 8418c2ecf20Sopenharmony_ci char *message = ptr; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (notify_socket == NULL) 8448c2ecf20Sopenharmony_ci return 0; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci mconsole_notify(notify_socket, MCONSOLE_PANIC, message, 8478c2ecf20Sopenharmony_ci strlen(message) + 1); 8488c2ecf20Sopenharmony_ci return 0; 8498c2ecf20Sopenharmony_ci} 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_cistatic struct notifier_block panic_exit_notifier = { 8528c2ecf20Sopenharmony_ci .notifier_call = notify_panic, 8538c2ecf20Sopenharmony_ci .next = NULL, 8548c2ecf20Sopenharmony_ci .priority = 1 8558c2ecf20Sopenharmony_ci}; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_cistatic int add_notifier(void) 8588c2ecf20Sopenharmony_ci{ 8598c2ecf20Sopenharmony_ci atomic_notifier_chain_register(&panic_notifier_list, 8608c2ecf20Sopenharmony_ci &panic_exit_notifier); 8618c2ecf20Sopenharmony_ci return 0; 8628c2ecf20Sopenharmony_ci} 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci__initcall(add_notifier); 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cichar *mconsole_notify_socket(void) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci return notify_socket; 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mconsole_notify_socket); 872