blob: 7290ad43ee272487d8401c95820ffde7fc1d5449 [file] [log] [blame]
From d12ed86dc1f0b243a986e109eee9deb79003d172 Mon Sep 17 00:00:00 2001
From: Andrew Bresticker <abrestic@chromium.org>
Date: Tue, 3 Nov 2015 14:27:00 -0800
Subject: [PATCH] CHROMIUM: ARM64: Add alt-syscall support
Add ARM64 support for changing a thread's syscall table at runtime using
the alt-syscall infrastructure.
BUG=b:25408860
BUG=b:29554103
TEST=Build and boot on kevin
Originally-Reviewed-on: https://chromium-review.googlesource.com/310601
Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
[rebase44(drinkcat):
- Conflict in arch/arm64/kernel/entry.S, change from adr to adrp by
0156411b1 "arm64: Implement the compat_sys_call_table in C"
- arch/arm64/include/asm/thread_info.h: fixup compat_sys_call_table type definition
- other minor context conflicts.]
Change-Id: I5d7a46a2d500a7e7e1834e42f463f7d862485bcb
Reviewed-on: https://chromium-review.googlesource.com/355410
Commit-Ready: Douglas Anderson <dianders@chromium.org>
Tested-by: Douglas Anderson <dianders@chromium.org>
Reviewed-by: Guenter Roeck <groeck@chromium.org>
Conflicts:
arch/arm64/Kconfig
arch/arm64/kernel/Makefile
arch/arm64/kernel/asm-offsets.c
[rebase412(groeck): Resolved conflicts; squashed FIXUP patches]
Signed-off-by: Guenter Roeck <groeck@chromium.org>
Conflicts:
arch/arm64/kernel/entry.S
[rebase414(groeck): Resolved conflicts; dropped exports]
Signed-off-by: Guenter Roeck <groeck@chromium.org>
Conflicts:
arch/arm64/include/asm/thread_info.h
arch/arm64/kernel/Makefile
arch/arm64/kernel/entry.S
[rebase419(groeck): Resolved conflicts;
syscall handling moved to C code]
Signed-off-by: Guenter Roeck <groeck@chromium.org>
Conflicts (context):
arch/arm64/Kconfig
arch/arm64/include/asm/thread_info.h
arch/arm64/kernel/Makefile
[rebase54(groeck): Context conflicts]
Signed-off-by: Guenter Roeck <groeck@chromium.org>
[rebase510(groeck): Context conflicts (arch/arm64/include/asm/thread_info.h)]
Signed-off-by: Guenter Roeck <groeck@chromium.org>
Change-Id: I7692e590e8d1eab9d0c72edd9692ba8f5582e523
---
arch/arm64/Kconfig | 1 +
arch/arm64/include/asm/syscall.h | 2 +
arch/arm64/include/asm/thread_info.h | 26 ++++++++++
arch/arm64/kernel/Makefile | 2 +
arch/arm64/kernel/alt-syscall.c | 78 ++++++++++++++++++++++++++++
arch/arm64/kernel/syscall.c | 14 +++++
include/linux/alt-syscall.h | 3 +-
7 files changed, 125 insertions(+), 1 deletion(-)
create mode 100644 arch/arm64/kernel/alt-syscall.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index e4e1b6550115..1849f8366a8a 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -15,6 +15,7 @@ config ARM64
select ARCH_HAS_DEBUG_VM_PGTABLE
select ARCH_HAS_DMA_PREP_COHERENT
select ARCH_HAS_ACPI_TABLE_UPGRADE if ACPI
+ select ARCH_HAS_ALT_SYSCALL
select ARCH_HAS_FAST_MULTIPLIER
select ARCH_HAS_FORTIFY_SOURCE
select ARCH_HAS_GCOV_PROFILE_ALL
diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h
index cfc0672013f6..5efc29166fb4 100644
--- a/arch/arm64/include/asm/syscall.h
+++ b/arch/arm64/include/asm/syscall.h
@@ -17,6 +17,8 @@ extern const syscall_fn_t sys_call_table[];
extern const syscall_fn_t compat_sys_call_table[];
#endif
+#define sys_call_ptr_t syscall_fn_t /* Needed for alt-syscall */
+
static inline int syscall_get_nr(struct task_struct *task,
struct pt_regs *regs)
{
diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h
index 6623c99f0984..8e35f63a3af2 100644
--- a/arch/arm64/include/asm/thread_info.h
+++ b/arch/arm64/include/asm/thread_info.h
@@ -17,6 +17,7 @@ struct task_struct;
#include <asm/memory.h>
#include <asm/stack_pointer.h>
#include <asm/types.h>
+#include <asm/unistd.h>
/*
* low level task data that entry.S needs immediate access to.
@@ -42,8 +43,32 @@ struct thread_info {
void *scs_base;
void *scs_sp;
#endif
+#ifdef CONFIG_ALT_SYSCALL
+ unsigned int nr_syscalls;
+ const void *sys_call_table;
+#ifdef CONFIG_COMPAT
+ unsigned int compat_nr_syscalls;
+ const void *compat_sys_call_table;
+#endif
+#endif
};
+#ifdef CONFIG_ALT_SYSCALL
+#ifdef CONFIG_COMPAT
+#define INIT_THREAD_INFO_SYSCALL_COMPAT \
+ .compat_nr_syscalls = __NR_compat_syscalls, \
+ .compat_sys_call_table = &compat_sys_call_table,
+#else
+#define INIT_THREAD_INFO_SYSCALL_COMPAT
+#endif
+#define INIT_THREAD_INFO_SYSCALL \
+ .nr_syscalls = __NR_syscalls, \
+ .sys_call_table = &sys_call_table, \
+ INIT_THREAD_INFO_SYSCALL_COMPAT
+#else
+#define INIT_THREAD_INFO_SYSCALL
+#endif
+
#define thread_saved_pc(tsk) \
((unsigned long)(tsk->thread.cpu_context.pc))
#define thread_saved_sp(tsk) \
@@ -120,6 +145,7 @@ int arch_dup_task_struct(struct task_struct *dst,
.flags = _TIF_FOREIGN_FPSTATE, \
.preempt_count = INIT_PREEMPT_COUNT, \
INIT_SCS \
+ INIT_THREAD_INFO_SYSCALL \
}
#endif /* __ASM_THREAD_INFO_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index ed65576ce710..3fe7daa352b1 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -62,6 +62,8 @@ obj-$(CONFIG_ARM64_MTE) += mte.o
obj-y += vdso-wrap.o
obj-$(CONFIG_COMPAT_VDSO) += vdso32-wrap.o
+obj-$(CONFIG_ALT_SYSCALL) += alt-syscall.o
+
obj-y += probes/
head-y := head.o
extra-y += $(head-y) vmlinux.lds
diff --git a/arch/arm64/kernel/alt-syscall.c b/arch/arm64/kernel/alt-syscall.c
new file mode 100644
index 000000000000..767c5371cf39
--- /dev/null
+++ b/arch/arm64/kernel/alt-syscall.c
@@ -0,0 +1,78 @@
+/*
+ * AArch64 alt-syscall implementation
+ *
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/alt-syscall.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+
+#include <asm/syscall.h>
+
+int arch_dup_sys_call_table(struct alt_sys_call_table *entry)
+{
+ int err;
+
+ if (!entry)
+ return -EINVAL;
+ if (entry->table)
+ return -EINVAL;
+#ifdef CONFIG_COMPAT
+ if (entry->compat_table)
+ return -EINVAL;
+#endif
+
+ entry->size = __NR_syscalls;
+ entry->table = kcalloc(entry->size, sizeof(sys_call_ptr_t), GFP_KERNEL);
+ if (!entry->table) {
+ err = -ENOMEM;
+ goto failed;
+ }
+ memcpy(entry->table, sys_call_table,
+ entry->size * sizeof(sys_call_ptr_t));
+
+#ifdef CONFIG_COMPAT
+ entry->compat_size = __NR_compat_syscalls;
+ entry->compat_table = kcalloc(entry->compat_size,
+ sizeof(sys_call_ptr_t), GFP_KERNEL);
+ if (!entry->compat_table) {
+ err = -ENOMEM;
+ goto compat_failed;
+ }
+ memcpy(entry->compat_table, compat_sys_call_table,
+ entry->compat_size * sizeof(sys_call_ptr_t));
+#endif
+
+ return 0;
+
+#ifdef CONFIG_COMPAT
+compat_failed:
+ entry->compat_size = 0;
+ kfree(entry->table);
+ entry->table = NULL;
+#endif
+failed:
+ entry->size = 0;
+ return err;
+}
+
+int arch_set_sys_call_table(struct alt_sys_call_table *entry)
+{
+ if (!entry)
+ return -EINVAL;
+
+ current_thread_info()->nr_syscalls = entry->size;
+ current_thread_info()->sys_call_table = entry->table;
+#ifdef CONFIG_COMPAT
+ current_thread_info()->compat_nr_syscalls = entry->compat_size;
+ current_thread_info()->compat_sys_call_table = entry->compat_table;
+#endif
+
+ return 0;
+}
diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c
index b9cf12b271d7..edaf7dbdfac6 100644
--- a/arch/arm64/kernel/syscall.c
+++ b/arch/arm64/kernel/syscall.c
@@ -164,14 +164,28 @@ static inline void sve_user_discard(void)
void do_el0_svc(struct pt_regs *regs)
{
+ struct thread_info __maybe_unused *ti;
+
sve_user_discard();
+#ifdef CONFIG_ALT_SYSCALL
+ ti = current_thread_info();
+ el0_svc_common(regs, regs->regs[8], ti->nr_syscalls,
+ ti->sys_call_table);
+#else
el0_svc_common(regs, regs->regs[8], __NR_syscalls, sys_call_table);
+#endif
}
#ifdef CONFIG_COMPAT
void do_el0_svc_compat(struct pt_regs *regs)
{
+#ifdef CONFIG_ALT_SYSCALL
+ struct thread_info *ti = current_thread_info();
+ el0_svc_common(regs, regs->regs[7], ti->compat_nr_syscalls,
+ ti->compat_sys_call_table);
+#else
el0_svc_common(regs, regs->regs[7], __NR_compat_syscalls,
compat_sys_call_table);
+#endif
}
#endif
diff --git a/include/linux/alt-syscall.h b/include/linux/alt-syscall.h
index 00f37c005eba..f817f89452e3 100644
--- a/include/linux/alt-syscall.h
+++ b/include/linux/alt-syscall.h
@@ -14,7 +14,8 @@ struct alt_sys_call_table {
char name[ALT_SYS_CALL_NAME_MAX + 1];
sys_call_ptr_t *table;
int size;
-#ifdef CONFIG_IA32_EMULATION
+#if defined(CONFIG_IA32_EMULATION) || \
+ (defined(CONFIG_ARM64) && defined(CONFIG_COMPAT))
sys_call_ptr_t *compat_table;
int compat_size;
#endif
--
2.17.1