Commit e0193fa4 authored by Charlie Jacobsen's avatar Charlie Jacobsen Committed by Vikram Narayanan

Basic lcd module create, run, and destroy.

This code is ugly, but it's working.

Tested with basic module, and appears to be working
properly. I will soon incorporate the patched
modprobe into the kernel tree, and then this code
will be usable by everyone.

The ipc code is still unimplemented. The only
hypercall handled is yield. Also note that other
exit conditions (e.g. external interrupt) have not
been fully tested.

Overview:
-- kernel code calls lcd_create_as_module with
   the module's name
-- lcd_create_as_module loads the module using
   request_lcd_module (request_lcd_module calls
   the patched modprobe to load the module, and
   the patched modprobe calls back into the lcd
   driver via the ioctrl interface to load the
   module)
-- lcd_create_as_module then finds the loaded
   module, spawns a kernel thread and passes off
   the module to it
-- the kernel thread initializes the lcd and
   maps the module inside it, then suspends itself
-- lcd_run_as_module wakes up the kernel thread
   and tells it to run
-- lcd_delete_as_module stops the kernel thread
   and deletes the module from the host kernel

File-by-file details:

arch/x86/include/asm/lcd-domains-arch.h
arch/x86/lcd-domains/lcd-domains-arch-tests.c
arch/x86/lcd-domains/lcd-domains-arch.c
-- lcd was not running in 64-bit mode, and my
   checks had one subtle bug
-- fixed %cr3 load to properly load vmcs first
-- fixed set program counter to use guest virtual
   rather than guest physical address

include/linux/sched.h
-- added struct lcd to task_struct

include/linux/init_task.h
-- lcd pointer set to null when task_struct is
   initialized

include/linux/module.h
kernel/module.c
-- made init_module and delete_module system calls
   callable from kernel code
-- available in module.h via do_sys_init_module and
   do_sys_delete_module
-- simply moved the majority of the guts of the
   system calls into a non-system call, exported
   routine
-- take an extra flag, for_lcd; when set, the init
   code skips over running (and deallocating) the
   module's init code, and the delete code skips
   over running the module exit
-- system calls from user code set for_lcd = 0; this
   ensures existing code still works

include/linux/kmod.h
kernel/kmod.c
kernel/sysctl.c
-- changed __request_module to __do_request_module; takes
   one extra argument, for_lcd
-- __request_module   ==>  __do_request_module with for_lcd = 0
-- request_lcd_module ==>  __do_request_module with for_lcd = 1
-- call_modprobe conditionally uses lcd_modprobe_path, the path
   to a patched modprobe accessible via sysfs

include/lcd-domains/lcd-domains.h
-- added lcd status enum; see source code doc
-- three routines for creating/running/destroying
   lcd's that use modules; see source code doc

include/uapi/linux/lcd-domains.h
-- added interface defns for patched modprobe to call into
   lcd driver for module init; lcd driver loads
   module (via slightly refactored module.c code) on behalf
   of modprobe

virt/lcd-domains/lcd-domains.c
-- implementation of routines for modules inside lcd's
-- implementation of module init / delete for lcd's
   (uses patched module.c code)

virt/lcd-domains/Kconfig
virt/lcd-domains/Makefile
virt/lcd-domains/lcd-module-load-test.c
virt/lcd-domains/lcd-tests.c
-- added test module for lcd module code
-- test runs automatically when lcd module is inserted
parent e2ddc9fa
...@@ -98,6 +98,10 @@ static inline void * hva2va(hva_t hva) ...@@ -98,6 +98,10 @@ static inline void * hva2va(hva_t hva)
{ {
return (void *)hva_val(hva); return (void *)hva_val(hva);
} }
static inline hva_t va2hva(void *va)
{
return __hva((unsigned long)va);
}
static inline hpa_t hva2hpa(hva_t hva) static inline hpa_t hva2hpa(hva_t hva)
{ {
return (hpa_t){ (unsigned long)__pa(hva2va(hva)) }; return (hpa_t){ (unsigned long)__pa(hva2va(hva)) };
...@@ -325,10 +329,10 @@ int lcd_arch_ept_unmap_range(struct lcd_arch *lcd, gpa_t ga_start, ...@@ -325,10 +329,10 @@ int lcd_arch_ept_unmap_range(struct lcd_arch *lcd, gpa_t ga_start,
*/ */
int lcd_arch_ept_gpa_to_hpa(struct lcd_arch *vcpu, gpa_t ga, hpa_t *ha_out); int lcd_arch_ept_gpa_to_hpa(struct lcd_arch *vcpu, gpa_t ga, hpa_t *ha_out);
/** /**
* Set the lcd's program counter to the guest physical address * Set the lcd's program counter to the guest virtual address
* a. * a.
*/ */
int lcd_arch_set_pc(struct lcd_arch *vcpu, gpa_t a); int lcd_arch_set_pc(struct lcd_arch *vcpu, gva_t a);
/** /**
* Set the lcd's gva root pointer (for x86, %cr3) to the * Set the lcd's gva root pointer (for x86, %cr3) to the
* guest physical address a. * guest physical address a.
......
...@@ -342,7 +342,7 @@ static int test08(void) ...@@ -342,7 +342,7 @@ static int test08(void)
printk(KERN_ERR "lcd arch: test08 error setting gva root\n"); printk(KERN_ERR "lcd arch: test08 error setting gva root\n");
goto fail4; goto fail4;
} }
ret = lcd_arch_set_pc(lcd, __gpa(0)); ret = lcd_arch_set_pc(lcd, __gva(0));
if (ret) { if (ret) {
printk(KERN_ERR "lcd arch: test08 error setting pc\n"); printk(KERN_ERR "lcd arch: test08 error setting pc\n");
goto fail5; goto fail5;
......
...@@ -1736,7 +1736,7 @@ static void vmx_setup_vmcs_guest_regs(struct lcd_arch *vcpu) ...@@ -1736,7 +1736,7 @@ static void vmx_setup_vmcs_guest_regs(struct lcd_arch *vcpu)
* *
* Intel SDM V3 24.4.1, 3.4.5, 26.3.1.2 * Intel SDM V3 24.4.1, 3.4.5, 26.3.1.2
*/ */
vmcs_writel(GUEST_CS_AR_BYTES, 0xD09B); vmcs_writel(GUEST_CS_AR_BYTES, 0xA09B);
vmcs_writel(GUEST_DS_AR_BYTES, 0x8093); vmcs_writel(GUEST_DS_AR_BYTES, 0x8093);
vmcs_writel(GUEST_ES_AR_BYTES, 0x8093); vmcs_writel(GUEST_ES_AR_BYTES, 0x8093);
vmcs_writel(GUEST_FS_AR_BYTES, 0x8093); vmcs_writel(GUEST_FS_AR_BYTES, 0x8093);
...@@ -2944,14 +2944,14 @@ int lcd_arch_run(struct lcd_arch *vcpu) ...@@ -2944,14 +2944,14 @@ int lcd_arch_run(struct lcd_arch *vcpu)
/* LCD RUNTIME ENV -------------------------------------------------- */ /* LCD RUNTIME ENV -------------------------------------------------- */
int lcd_arch_set_pc(struct lcd_arch *vcpu, gpa_t a) int lcd_arch_set_pc(struct lcd_arch *vcpu, gva_t a)
{ {
vcpu->regs[LCD_ARCH_REGS_RIP] = gpa_val(a); vcpu->regs[LCD_ARCH_REGS_RIP] = gva_val(a);
/* /*
* Must load vmcs to modify it * Must load vmcs to modify it
*/ */
vmx_get_cpu(vcpu); vmx_get_cpu(vcpu);
vmcs_writel(GUEST_RIP, gpa_val(a)); vmcs_writel(GUEST_RIP, gva_val(a));
vmx_put_cpu(vcpu); vmx_put_cpu(vcpu);
return 0; return 0;
} }
...@@ -2961,7 +2961,12 @@ int lcd_arch_set_gva_root(struct lcd_arch *vcpu, gpa_t a) ...@@ -2961,7 +2961,12 @@ int lcd_arch_set_gva_root(struct lcd_arch *vcpu, gpa_t a)
u64 cr3_ptr; u64 cr3_ptr;
cr3_ptr = gpa_val(a); /* no page write through, etc. ... */ cr3_ptr = gpa_val(a); /* no page write through, etc. ... */
/*
* Must load vmcs to modify it
*/
vmx_get_cpu(vcpu);
vmcs_writel(GUEST_CR3, cr3_ptr); vmcs_writel(GUEST_CR3, cr3_ptr);
vmx_put_cpu(vcpu);
return 0; return 0;
} }
...@@ -4137,7 +4142,7 @@ static int vmx_check_guest_seg(struct lcd_arch *vcpu) ...@@ -4137,7 +4142,7 @@ static int vmx_check_guest_seg(struct lcd_arch *vcpu)
*/ */
act64 = vmx_getl(vcpu, GUEST_CS_AR_BYTES); act64 = vmx_getl(vcpu, GUEST_CS_AR_BYTES);
if (vmx_entry_has(vcpu, VM_ENTRY_IA32E_MODE) && if (vmx_entry_has(vcpu, VM_ENTRY_IA32E_MODE) &&
vmx_seg_l_mode(act64) && !vmx_seg_db(act64)) { vmx_seg_l_mode(act64) && vmx_seg_db(act64)) {
printk(KERN_ERR "lcd vmx: guest cs improper db/l-mode bits\n"); printk(KERN_ERR "lcd vmx: guest cs improper db/l-mode bits\n");
return -1; return -1;
} }
...@@ -4308,11 +4313,11 @@ static int vmx_check_guest_rip_rflags(struct lcd_arch *vcpu) ...@@ -4308,11 +4313,11 @@ static int vmx_check_guest_rip_rflags(struct lcd_arch *vcpu)
return -1; return -1;
} }
} else { } else {
lin_addr_width = (cpuid_eax(0x80000008) >> 7) & 0xff; lin_addr_width = (cpuid_eax(0x80000008) >> 8) & 0xff;
sact64 = (signed long)act64; sact64 = (signed long)act64;
if ((sact64 >> lin_addr_width) != 0 || if ((sact64 >> lin_addr_width) != 0 &&
(sact64 >> lin_addr_width != -1)) { (sact64 >> lin_addr_width) != -1) {
printk(KERN_ERR "lcd vmx: guest rip exceeds max linear addr\n"); printk(KERN_ERR "lcd vmx: guest rip 0x%llx exceeds max linear addr\n", act64);
return -1; return -1;
} }
} }
......
#ifndef LCD_DOMAINS_LCD_DOMAINS_H #ifndef LCD_DOMAINS_LCD_DOMAINS_H
#define LCD_DOMAINS_LCD_DOMAINS_H #define LCD_DOMAINS_LCD_DOMAINS_H
#include <linux/module.h>
#include <asm/lcd-domains-arch.h> #include <asm/lcd-domains-arch.h>
/*
* lcd_status = status of kthread / lcd
* ====================================
*
* LCD_STATUS_UNFORMED = still setting up, or status not set yet
* LCD_STATUS_SUSPENDED = lcd is paused, and kthread is going to asleep / is
* asleep
* LCD_STATUS_RUNNABLE = lcd is paused, and kthread is awake, or should awaken
* (the status should be set to this if the lcd /
* kthread are suspended, and you want it to wake up)
* LCD_STATUS_RUNNING = lcd is running, or is about to run
* LCD_STATUS_KILL = lcd and kthread should die
* LCD_STATUS_DEAD = lcd is not running, most parts have been destroyed
* (lcd_struct is only hanging around to provide status
* info); the kthread is ready to die and be reaped
*/
enum lcd_status {
LCD_STATUS_UNFORMED = 0,
LCD_STATUS_SUSPENDED = 1,
LCD_STATUS_RUNNABLE = 2,
LCD_STATUS_RUNNING = 3,
LCD_STATUS_KILL = 4,
LCD_STATUS_DEAD = 5,
};
struct lcd { struct lcd {
/*
* Display name
*/
char name[MODULE_NAME_LEN];
/*
* Status (enum lcd_status)
*/
int status;
/* /*
* Arch-dependent state of lcd * Arch-dependent state of lcd
*/ */
...@@ -40,4 +74,36 @@ struct lcd { ...@@ -40,4 +74,36 @@ struct lcd {
} gv; } gv;
}; };
/**
* -- Loads module_name into host kernel. (Note: The module loading code
* expects underscores, _, rather than hyphens. If the module's name
* in the file system is some-module.ko, use the name some_module.)
* -- Spawns a kernel thread that will host the lcd.
* -- The kernel thread will create the lcd and map the module into
* the lcd. The kernel thread will then wait with the lcd's status
* set to LCD_STATUS_SUSPENDED.
* -- Call lcd_run_as_module to start running the lcd.
* -- Returns NULL if we fail to create the kernel thread, or if the
* kernel thread failed to initialize the lcd, etc.
*
* Call lcd_destroy_as_module after a successful return from
* lcd_create_as_module to stop the kthread and remove the module
* from the host kernel.
*/
struct task_struct * lcd_create_as_module(char *module_name);
/**
* Wakes up kthread to start running lcd. Call this after a successful
* return from lcd_create_as_module. Call lcd_destroy_as_module when
* the kthread/lcd are no longer needed.
*/
int lcd_run_as_module(struct task_struct *t);
/**
* Stops the kernel thread (which in turn, destroys the lcd) and removes
* the module from the host kernel.
*
* Note: The kthread checks if it should stop each time the lcd exits in
* the main run loop.
*/
void lcd_destroy_as_module(struct task_struct *t, char *module_name);
#endif /* LCD_DOMAINS_LCD_DOMAINS_H */ #endif /* LCD_DOMAINS_LCD_DOMAINS_H */
...@@ -15,12 +15,11 @@ ...@@ -15,12 +15,11 @@
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <linux/sched/rt.h> #include <linux/sched/rt.h>
#ifdef CONFIG_LCD #ifdef CONFIG_HAVE_LCD
#define INIT_LCD(tsk) \ #include <lcd-domains/lcd-domains.h>
.sync_rendezvous = LIST_HEAD_INIT(tsk.sync_rendezvous), \ #define INIT_LCD .lcd = NULL,
.utcb = NULL,
#else #else
#define INIT_LCD(tsk) #define INIT_LCD
#endif #endif
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
...@@ -254,7 +253,7 @@ extern struct task_group root_task_group; ...@@ -254,7 +253,7 @@ extern struct task_group root_task_group;
}, \ }, \
.thread_group = LIST_HEAD_INIT(tsk.thread_group), \ .thread_group = LIST_HEAD_INIT(tsk.thread_group), \
.thread_node = LIST_HEAD_INIT(init_signals.thread_head), \ .thread_node = LIST_HEAD_INIT(init_signals.thread_head), \
.cspace = NULL, \ INIT_LCD \
INIT_IDS \ INIT_IDS \
INIT_PERF_EVENTS(tsk) \ INIT_PERF_EVENTS(tsk) \
INIT_TRACE_IRQFLAGS \ INIT_TRACE_IRQFLAGS \
......
...@@ -30,12 +30,16 @@ ...@@ -30,12 +30,16 @@
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
extern char modprobe_path[]; /* for sysctl */ extern char modprobe_path[]; /* for sysctl */
extern char lcd_modprobe_path[]; /* for sysctl */
/* modprobe exit status on success, -ve on error. Return value /* modprobe exit status on success, -ve on error. Return value
* usually useless though. */ * usually useless though. */
extern __printf(2, 3) extern __printf(3, 4)
int __request_module(bool wait, const char *name, ...); int __do_request_module(bool wait, int for_lcd, const char *name, ...);
#define __request_module(wait, mod...) __do_request_module(wait,0,mod)
#define request_module(mod...) __request_module(true, mod) #define request_module(mod...) __request_module(true, mod)
#define request_module_nowait(mod...) __request_module(false, mod) #define request_module_nowait(mod...) __request_module(false, mod)
#define request_lcd_module(mod...) __do_request_module(true,1,mod)
#define try_then_request_module(x, mod...) \ #define try_then_request_module(x, mod...) \
((x) ?: (__request_module(true, mod), (x))) ((x) ?: (__request_module(true, mod), (x)))
#else #else
......
...@@ -1470,10 +1470,7 @@ struct task_struct { ...@@ -1470,10 +1470,7 @@ struct task_struct {
unsigned int flags; /* per process flags, defined below */ unsigned int flags; /* per process flags, defined below */
unsigned int ptrace; unsigned int ptrace;
#ifdef CONFIG_HAVE_LCD #ifdef CONFIG_HAVE_LCD
struct cspace cspace; struct lcd *lcd;
struct cap_cache cap_cache;
struct list_head sync_rendezvous;
struct utcb *utcb;
#endif #endif
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
struct llist_node wake_entry; struct llist_node wake_entry;
......
...@@ -25,7 +25,15 @@ struct lcd_blob_info { ...@@ -25,7 +25,15 @@ struct lcd_blob_info {
unsigned int blob_order; unsigned int blob_order;
} __attribute__((packed)); } __attribute__((packed));
struct lcd_init_module_args {
/* syscall arguments to init_module */
void *module_image;
unsigned long len;
const char *param_values;
} __attribute__((packed));
#define LCD_LOAD_PV_KERNEL _IOR(LCD_MINOR, 0x01, struct lcd_pv_kernel_config) #define LCD_LOAD_PV_KERNEL _IOR(LCD_MINOR, 0x01, struct lcd_pv_kernel_config)
#define LCD_RUN_BLOB _IOR(LCD_MINOR, 0x02, struct lcd_blob_info) #define LCD_RUN_BLOB _IOR(LCD_MINOR, 0x02, struct lcd_blob_info)
#define LCD_INIT_MODULE _IOR(LCD_MINOR, 0x03, struct lcd_init_module_args)
#endif /* LCD_DOMAINS_H */ #endif /* LCD_DOMAINS_H */
...@@ -56,9 +56,10 @@ static DECLARE_RWSEM(umhelper_sem); ...@@ -56,9 +56,10 @@ static DECLARE_RWSEM(umhelper_sem);
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
/* /*
modprobe_path is set via /proc/sys. modprobe_path and lcd_modprobe_path is set via /proc/sys.
*/ */
char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe"; char modprobe_path[KMOD_PATH_LEN] = "/sbin/modprobe";
char lcd_modprobe_path[KMOD_PATH_LEN] = "/sbin/lcd-modprobe";
static void free_modprobe_argv(struct subprocess_info *info) static void free_modprobe_argv(struct subprocess_info *info)
{ {
...@@ -66,7 +67,7 @@ static void free_modprobe_argv(struct subprocess_info *info) ...@@ -66,7 +67,7 @@ static void free_modprobe_argv(struct subprocess_info *info)
kfree(info->argv); kfree(info->argv);
} }
static int call_modprobe(char *module_name, int wait) static int call_modprobe(char *module_name, int wait, int for_lcd)
{ {
struct subprocess_info *info; struct subprocess_info *info;
static char *envp[] = { static char *envp[] = {
...@@ -77,6 +78,7 @@ static int call_modprobe(char *module_name, int wait) ...@@ -77,6 +78,7 @@ static int call_modprobe(char *module_name, int wait)
}; };
char **argv = kmalloc(sizeof(char *[5]), GFP_KERNEL); char **argv = kmalloc(sizeof(char *[5]), GFP_KERNEL);
char *__modprobe_path = for_lcd ? lcd_modprobe_path : modprobe_path;
if (!argv) if (!argv)
goto out; goto out;
...@@ -84,14 +86,15 @@ static int call_modprobe(char *module_name, int wait) ...@@ -84,14 +86,15 @@ static int call_modprobe(char *module_name, int wait)
if (!module_name) if (!module_name)
goto free_argv; goto free_argv;
argv[0] = modprobe_path; argv[0] = __modprobe_path;
argv[1] = "-q"; argv[1] = "-q";
argv[2] = "--"; argv[2] = "--";
argv[3] = module_name; /* check free_modprobe_argv() */ argv[3] = module_name; /* check free_modprobe_argv() */
argv[4] = NULL; argv[4] = NULL;
info = call_usermodehelper_setup(modprobe_path, argv, envp, GFP_KERNEL, info = call_usermodehelper_setup(__modprobe_path, argv, envp,
NULL, free_modprobe_argv, NULL); GFP_KERNEL, NULL, free_modprobe_argv,
NULL);
if (!info) if (!info)
goto free_module_name; goto free_module_name;
...@@ -106,8 +109,9 @@ out: ...@@ -106,8 +109,9 @@ out:
} }
/** /**
* __request_module - try to load a kernel module * __do_request_module - try to load a kernel module
* @wait: wait (or not) for the operation to complete * @wait: wait (or not) for the operation to complete
* @for_lcd: non-zero if module will be exec'd in lcd
* @fmt: printf style format string for the name of the module * @fmt: printf style format string for the name of the module
* @...: arguments as specified in the format string * @...: arguments as specified in the format string
* *
...@@ -121,7 +125,7 @@ out: ...@@ -121,7 +125,7 @@ out:
* If module auto-loading support is disabled then this function * If module auto-loading support is disabled then this function
* becomes a no-operation. * becomes a no-operation.
*/ */
int __request_module(bool wait, const char *fmt, ...) int __do_request_module(bool wait, int for_lcd, const char *fmt, ...)
{ {
va_list args; va_list args;
char module_name[MODULE_NAME_LEN]; char module_name[MODULE_NAME_LEN];
...@@ -180,12 +184,13 @@ int __request_module(bool wait, const char *fmt, ...) ...@@ -180,12 +184,13 @@ int __request_module(bool wait, const char *fmt, ...)
trace_module_request(module_name, wait, _RET_IP_); trace_module_request(module_name, wait, _RET_IP_);
ret = call_modprobe(module_name, wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC); ret = call_modprobe(module_name, wait ? UMH_WAIT_PROC : UMH_WAIT_EXEC,
for_lcd);
atomic_dec(&kmod_concurrent); atomic_dec(&kmod_concurrent);
return ret; return ret;
} }
EXPORT_SYMBOL(__request_module); EXPORT_SYMBOL(__do_request_module);
#endif /* CONFIG_MODULES */ #endif /* CONFIG_MODULES */
static void call_usermodehelper_freeinfo(struct subprocess_info *info) static void call_usermodehelper_freeinfo(struct subprocess_info *info)
......
...@@ -932,16 +932,12 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user, ...@@ -932,16 +932,12 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
unsigned int, flags) unsigned int, flags)
{ {
struct module *mod; struct module *mod;
char name[MODULE_NAME_LEN];
int ret, forced = 0; int ret, forced = 0;
if (!capable(CAP_SYS_MODULE) || modules_disabled) if (!capable(CAP_SYS_MODULE) || modules_disabled)
return -EPERM; return -EPERM;
if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0)
return -EFAULT;
name[MODULE_NAME_LEN-1] = '\0';
if (mutex_lock_interruptible(&module_mutex) != 0) if (mutex_lock_interruptible(&module_mutex) != 0)
return -EINTR; return -EINTR;
...@@ -981,8 +977,9 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user, ...@@ -981,8 +977,9 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
goto out; goto out;
mutex_unlock(&module_mutex); mutex_unlock(&module_mutex);
/* Final destruction now no one is using it. */ /* Final destruction now no one is using it. */
if (mod->exit != NULL) if (mod->exit != NULL && !for_lcd)
mod->exit(); mod->exit();
blocking_notifier_call_chain(&module_notify_list, blocking_notifier_call_chain(&module_notify_list,
MODULE_STATE_GOING, mod); MODULE_STATE_GOING, mod);
...@@ -1000,6 +997,19 @@ out: ...@@ -1000,6 +997,19 @@ out:
mutex_unlock(&module_mutex); mutex_unlock(&module_mutex);
return ret; return ret;
} }
EXPORT_SYMBOL(do_sys_delete_module);
SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
unsigned int, flags)
{
char name[MODULE_NAME_LEN];
if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0)
return -EFAULT;
name[MODULE_NAME_LEN-1] = '\0';
return do_sys_delete_module(name, flags, 0);
}
static inline void print_unload_info(struct seq_file *m, struct module *mod) static inline void print_unload_info(struct seq_file *m, struct module *mod)
{ {
...@@ -3357,7 +3367,7 @@ static void do_free_init(struct rcu_head *head) ...@@ -3357,7 +3367,7 @@ static void do_free_init(struct rcu_head *head)
* Keep it uninlined to provide a reliable breakpoint target, e.g. for the gdb * Keep it uninlined to provide a reliable breakpoint target, e.g. for the gdb
* helper command 'lx-symbols'. * helper command 'lx-symbols'.
*/ */
static noinline int do_init_module(struct module *mod) static noinline int do_init_module(struct module *mod, int for_lcd)
{ {
int ret = 0; int ret = 0;
struct mod_initfree *freeinit; struct mod_initfree *freeinit;
...@@ -3377,7 +3387,7 @@ static noinline int do_init_module(struct module *mod) ...@@ -3377,7 +3387,7 @@ static noinline int do_init_module(struct module *mod)
do_mod_ctors(mod); do_mod_ctors(mod);
/* Start the module */ /* Start the module */
if (mod->init != NULL) if (mod->init != NULL && !for_lcd)
ret = do_one_initcall(mod->init); ret = do_one_initcall(mod->init);
if (ret < 0) { if (ret < 0) {
goto fail_free_freeinit; goto fail_free_freeinit;
...@@ -3439,6 +3449,19 @@ static noinline int do_init_module(struct module *mod) ...@@ -3439,6 +3449,19 @@ static noinline int do_init_module(struct module *mod)
* path, so use actual RCU here. * path, so use actual RCU here.
*/ */
call_rcu_sched(&freeinit->rcu, do_free_init); call_rcu_sched(&freeinit->rcu, do_free_init);
if (!for_lcd) {
/*
* Only free init code if we're not going to run in
* an lcd. If we will run in an lcd, init code will
* be deallocated via free_module in do_sys_delete_module.
*/
unset_module_init_ro_nx(mod);
module_free(mod, mod->module_init);
mod->module_init = NULL;
mod->init_size = 0;
mod->init_ro_size = 0;
mod->init_text_size = 0;
}
mutex_unlock(&module_mutex); mutex_unlock(&module_mutex);
wake_up_all(&module_wq); wake_up_all(&module_wq);
...@@ -3572,7 +3595,7 @@ static int unknown_module_param_cb(char *param, char *val, const char *modname, ...@@ -3572,7 +3595,7 @@ static int unknown_module_param_cb(char *param, char *val, const char *modname,
/* Allocate and load the module: note that size of section 0 is always /* Allocate and load the module: note that size of section 0 is always
zero, and we rely on this for optional sections. */ zero, and we rely on this for optional sections. */
static int load_module(struct load_info *info, const char __user *uargs, static int load_module(struct load_info *info, const char __user *uargs,
int flags) int flags, int for_lcd)
{ {
struct module *mod; struct module *mod;
long err; long err;
...@@ -3857,10 +3880,7 @@ static int load_lcd(struct load_info *info, const char __user *uargs, ...@@ -3857,10 +3880,7 @@ static int load_lcd(struct load_info *info, const char __user *uargs,
/* Done! */ /* Done! */
trace_module_load(mod); trace_module_load(mod);
lcd = lcd_create(); return do_init_module(mod, for_lcd);
lcd_move_module(lcd, mod);
lcd_run(lcd);
return 0;
bug_cleanup: bug_cleanup:
/* module_bug_cleanup needs module_mutex protection */ /* module_bug_cleanup needs module_mutex protection */
...@@ -3913,8 +3933,8 @@ SYSCALL_DEFINE3(init_lcd, void __user *, umod, ...@@ -3913,8 +3933,8 @@ SYSCALL_DEFINE3(init_lcd, void __user *, umod,
} }
#endif #endif
SYSCALL_DEFINE3(init_module, void __user *, umod, int do_sys_init_module(void __user *umod, unsigned long len,
unsigned long, len, const char __user *, uargs) const char __user *uargs, int for_lcd)
{ {
int err; int err;
struct load_info info = { }; struct load_info info = { };
...@@ -3930,7 +3950,14 @@ SYSCALL_DEFINE3(init_module, void __user *, umod, ...@@ -3930,7 +3950,14 @@ SYSCALL_DEFINE3(init_module, void __user *, umod,
if (err) if (err)
return err; return err;
return load_module(&info, uargs, 0); return load_module(&info, uargs, 0, for_lcd);
}
EXPORT_SYMBOL(do_sys_init_module);
SYSCALL_DEFINE3(init_module, void __user *, umod,
unsigned long, len, const char __user *, uargs)
{
return do_sys_init_module(umod, len, uargs, 0);
} }
SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags) SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags)
...@@ -3957,7 +3984,7 @@ SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags) ...@@ -3957,7 +3984,7 @@ SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags)
info.hdr = hdr; info.hdr = hdr;
info.len = size; info.len = size;