summaryrefslogtreecommitdiff
path: root/kernel/exec_domain.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@athlon.transmeta.com>2002-02-04 17:40:40 -0800
committerLinus Torvalds <torvalds@athlon.transmeta.com>2002-02-04 17:40:40 -0800
commit7a2deb32924142696b8174cdf9b38cd72a11fc96 (patch)
tree8ecc18f81fdb849254f39dc2e9fd77253319e1ec /kernel/exec_domain.c
Import changeset
Diffstat (limited to 'kernel/exec_domain.c')
-rw-r--r--kernel/exec_domain.c163
1 files changed, 163 insertions, 0 deletions
diff --git a/kernel/exec_domain.c b/kernel/exec_domain.c
new file mode 100644
index 000000000000..1daf64cc19b6
--- /dev/null
+++ b/kernel/exec_domain.c
@@ -0,0 +1,163 @@
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/module.h>
+
+static asmlinkage void no_lcall7(int segment, struct pt_regs * regs);
+
+
+static unsigned long ident_map[32] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31
+};
+
+struct exec_domain default_exec_domain = {
+ "Linux", /* name */
+ no_lcall7, /* lcall7 causes a seg fault. */
+ 0, 0xff, /* All personalities. */
+ ident_map, /* Identity map signals. */
+ ident_map, /* - both ways. */
+ NULL, /* No usage counter. */
+ NULL /* Nothing after this in the list. */
+};
+
+static struct exec_domain *exec_domains = &default_exec_domain;
+static rwlock_t exec_domains_lock = RW_LOCK_UNLOCKED;
+
+static asmlinkage void no_lcall7(int segment, struct pt_regs * regs)
+{
+ /*
+ * This may have been a static linked SVr4 binary, so we would have the
+ * personality set incorrectly. Check to see whether SVr4 is available,
+ * and use it, otherwise give the user a SEGV.
+ */
+ set_personality(PER_SVR4);
+
+ if (current->exec_domain && current->exec_domain->handler
+ && current->exec_domain->handler != no_lcall7) {
+ current->exec_domain->handler(segment, regs);
+ return;
+ }
+
+ send_sig(SIGSEGV, current, 1);
+}
+
+static struct exec_domain *lookup_exec_domain(unsigned long personality)
+{
+ unsigned long pers = personality & PER_MASK;
+ struct exec_domain *it;
+
+ read_lock(&exec_domains_lock);
+ for (it=exec_domains; it; it=it->next)
+ if (pers >= it->pers_low && pers <= it->pers_high) {
+ if (!try_inc_mod_count(it->module))
+ continue;
+ read_unlock(&exec_domains_lock);
+ return it;
+ }
+ read_unlock(&exec_domains_lock);
+
+ /* Should never get this far. */
+ printk(KERN_ERR "No execution domain for personality 0x%02lx\n", pers);
+ return NULL;
+}
+
+int register_exec_domain(struct exec_domain *it)
+{
+ struct exec_domain *tmp;
+
+ if (!it)
+ return -EINVAL;
+ if (it->next)
+ return -EBUSY;
+ write_lock(&exec_domains_lock);
+ for (tmp=exec_domains; tmp; tmp=tmp->next)
+ if (tmp == it) {
+ write_unlock(&exec_domains_lock);
+ return -EBUSY;
+ }
+ it->next = exec_domains;
+ exec_domains = it;
+ write_unlock(&exec_domains_lock);
+ return 0;
+}
+
+int unregister_exec_domain(struct exec_domain *it)
+{
+ struct exec_domain ** tmp;
+
+ tmp = &exec_domains;
+ write_lock(&exec_domains_lock);
+ while (*tmp) {
+ if (it == *tmp) {
+ *tmp = it->next;
+ it->next = NULL;
+ write_unlock(&exec_domains_lock);
+ return 0;
+ }
+ tmp = &(*tmp)->next;
+ }
+ write_unlock(&exec_domains_lock);
+ return -EINVAL;
+}
+
+void __set_personality(unsigned long personality)
+{
+ struct exec_domain *it, *prev;
+
+ it = lookup_exec_domain(personality);
+ if (it == current->exec_domain) {
+ current->personality = personality;
+ return;
+ }
+ if (!it)
+ return;
+ if (atomic_read(&current->fs->count) != 1) {
+ struct fs_struct *new = copy_fs_struct(current->fs);
+ struct fs_struct *old;
+ if (!new) {
+ put_exec_domain(it);
+ return;
+ }
+ task_lock(current);
+ old = current->fs;
+ current->fs = new;
+ task_unlock(current);
+ put_fs_struct(old);
+ }
+ /*
+ * At that point we are guaranteed to be the sole owner of
+ * current->fs.
+ */
+ current->personality = personality;
+ prev = current->exec_domain;
+ current->exec_domain = it;
+ set_fs_altroot();
+ put_exec_domain(prev);
+}
+
+asmlinkage long sys_personality(unsigned long personality)
+{
+ int ret = current->personality;
+ if (personality != 0xffffffff) {
+ set_personality(personality);
+ if (current->personality != personality)
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+int get_exec_domain_list(char * page)
+{
+ int len = 0;
+ struct exec_domain * e;
+
+ read_lock(&exec_domains_lock);
+ for (e=exec_domains; e && len < PAGE_SIZE - 80; e=e->next)
+ len += sprintf(page+len, "%d-%d\t%-16s\t[%s]\n",
+ e->pers_low, e->pers_high, e->name,
+ e->module ? e->module->name : "kernel");
+ read_unlock(&exec_domains_lock);
+ return len;
+}