diff options
author | Amit Pundir <amit.pundir@linaro.org> | 2015-02-20 12:17:40 +0530 |
---|---|---|
committer | Amit Pundir <amit.pundir@linaro.org> | 2015-02-20 13:21:54 +0530 |
commit | 7ffdd3fae2e1917268ed35b76ec905ed5b166aa6 (patch) | |
tree | 5b45c802ffc3ce5d97b8f990bb2b69996bdac478 | |
parent | 37d37744341e65b39bf6e8141ba3b46c8a0772f0 (diff) | |
parent | eec2486674b53d8ca576677cae60bc3f380d33c1 (diff) |
Merge branch 'linaro-android-3.14-lsk' into linux-linaro-lsk-v3.14-android
* linaro-android-3.14-lsk: (52 commits)
xt_qtaguid: use sock_gen_put() instead of xt_socket_put_sk()
android: base-cfg: enable ARMV8_DEPRECATED and subfeatures
arm64: kconfig: move emulation option under kernel features
kbuild: make it possible to specify the module output dir
arm64: Emulate SETEND for AArch32 tasks
arm64: Consolidate hotplug notifier for instruction emulation
arm64: kernel: explicitly add include in armv8_deprecated
arm64: Track system support for mixed endian EL0
arm64: Fix up /proc/cpuinfo
arm64: cpuinfo: record cpu system register values
arm64: add MIDR_EL1 field accessors
arm64: fix return code check when changing emulation handler
arm64: Trace emulation of AArch32 legacy instructions
arm64: Emulate CP15 Barrier instructions
arm64: barriers: add dmb barrier
arm64: Port SWP/SWPB emulation support from arm
arm64: Add framework for legacy instruction emulation
arm64: Add AArch32 instruction set condition code checks
arm64: Add support for hooks to handle undefined instructions
arm64: kernel: Explicitly add include in traps.c
...
Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
Conflicts:
arch/arm64/include/asm/barrier.h
arch/arm64/include/asm/cputype.h
arch/arm64/include/asm/insn.h
arch/arm64/kernel/Makefile
arch/arm64/kernel/setup.c
44 files changed, 1727 insertions, 785 deletions
diff --git a/Documentation/arm64/legacy_instructions.txt b/Documentation/arm64/legacy_instructions.txt new file mode 100644 index 000000000000..01bf3d9fac85 --- /dev/null +++ b/Documentation/arm64/legacy_instructions.txt @@ -0,0 +1,57 @@ +The arm64 port of the Linux kernel provides infrastructure to support +emulation of instructions which have been deprecated, or obsoleted in +the architecture. The infrastructure code uses undefined instruction +hooks to support emulation. Where available it also allows turning on +the instruction execution in hardware. + +The emulation mode can be controlled by writing to sysctl nodes +(/proc/sys/abi). The following explains the different execution +behaviours and the corresponding values of the sysctl nodes - + +* Undef + Value: 0 + Generates undefined instruction abort. Default for instructions that + have been obsoleted in the architecture, e.g., SWP + +* Emulate + Value: 1 + Uses software emulation. To aid migration of software, in this mode + usage of emulated instruction is traced as well as rate limited + warnings are issued. This is the default for deprecated + instructions, .e.g., CP15 barriers + +* Hardware Execution + Value: 2 + Although marked as deprecated, some implementations may support the + enabling/disabling of hardware support for the execution of these + instructions. Using hardware execution generally provides better + performance, but at the loss of ability to gather runtime statistics + about the use of the deprecated instructions. + +The default mode depends on the status of the instruction in the +architecture. Deprecated instructions should default to emulation +while obsolete instructions must be undefined by default. + +Note: Instruction emulation may not be possible in all cases. See +individual instruction notes for further information. + +Supported legacy instructions +----------------------------- +* SWP{B} +Node: /proc/sys/abi/swp +Status: Obsolete +Default: Undef (0) + +* CP15 Barriers +Node: /proc/sys/abi/cp15_barrier +Status: Deprecated +Default: Emulate (1) + +* SETEND +Node: /proc/sys/abi/setend +Status: Deprecated +Default: Emulate (1)* +Note: All the cpus on the system must have mixed endian support at EL0 +for this feature to be enabled. If a new CPU - which doesn't support mixed +endian - is hotplugged in after this feature has been enabled, there could +be unexpected results in the application. diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg index d8503e450957..d9857d2cd32a 100644 --- a/android/configs/android-base.cfg +++ b/android/configs/android-base.cfg @@ -6,7 +6,7 @@ CONFIG_ANDROID=y CONFIG_ANDROID_BINDER_IPC=y CONFIG_ANDROID_INTF_ALARM_DEV=y CONFIG_ANDROID_LOW_MEMORY_KILLER=y -CONFIG_ARMV7_COMPAT=y +CONFIG_ARMV8_DEPRECATED=y CONFIG_ASHMEM=y CONFIG_BLK_DEV_DM=y CONFIG_BLK_DEV_INITRD=y @@ -15,6 +15,7 @@ CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_DEBUG=y CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_SCHED=y +CONFIG_CP15_BARRIER_EMULATION=y CONFIG_DM_CRYPT=y CONFIG_DM_VERITY=y CONFIG_EMBEDDED=y @@ -129,8 +130,10 @@ CONFIG_PREEMPT=y CONFIG_RESOURCE_COUNTERS=y CONFIG_RTC_CLASS=y CONFIG_RT_GROUP_SCHED=y +CONFIG_SETEND_EMULATION=y CONFIG_STAGING=y CONFIG_SWITCH=y +CONFIG_SWP_EMULATION=y CONFIG_SYNC=y CONFIG_SYSVIPC=y CONFIG_TUN=y diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 75694b2ca17a..12de67a9a463 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -328,31 +328,6 @@ config HOTPLUG_CPU Say Y here to experiment with turning CPUs off and on. CPUs can be controlled through /sys/devices/system/cpu. -config SWP_EMULATE - bool "Emulate SWP/SWPB instructions" - help - ARMv6 architecture deprecates use of the SWP/SWPB instructions. ARMv8 - oblosetes the use of SWP/SWPB instructions. ARMv7 multiprocessing - extensions introduce the ability to disable these instructions, - triggering an undefined instruction exception when executed. Say Y - here to enable software emulation of these instructions for userspace - (not kernel) using LDREX/STREX. Also creates /proc/cpu/swp_emulation - for statistics. - - In some older versions of glibc [<=2.8] SWP is used during futex - trylock() operations with the assumption that the code will not - be preempted. This invalid assumption may be more likely to fail - with SWP emulation enabled, leading to deadlock of the user - application. - - NOTE: when accessing uncached shared regions, LDREX/STREX rely - on an external transaction monitoring block called a global - monitor to maintain update atomicity. If your system does not - implement a global monitor, this option can cause programs that - perform SWP operations to uncached memory to deadlock. - - If unsure, say Y. - source kernel/Kconfig.preempt config HZ @@ -383,27 +358,6 @@ config HW_PERF_EVENTS Enable hardware performance counter support for perf events. If disabled, perf events will use software events only. -config ARMV7_COMPAT - bool "Kernel support for ARMv7 applications" - depends on COMPAT - select SWP_EMULATE - help - This option enables features that allow that ran on an ARMv7 or older - processor to continue functioning. - - If you want to execute ARMv7 applications, say Y - -config ARMV7_COMPAT_CPUINFO - bool "Report backwards compatible cpu features in /proc/cpuinfo" - depends on ARMV7_COMPAT - default y - help - This option makes /proc/cpuinfo list CPU features that an ARMv7 or - earlier kernel would report, but are not optional on an ARMv8 or later - processor. - - If you want to execute ARMv7 applications, say Y - config SYS_SUPPORTS_HUGETLBFS def_bool y @@ -468,6 +422,74 @@ config SECCOMP and the task is only allowed to execute a few safe syscalls defined by each seccomp mode. +menuconfig ARMV8_DEPRECATED + bool "Emulate deprecated/obsolete ARMv8 instructions" + depends on COMPAT + help + Legacy software support may require certain instructions + that have been deprecated or obsoleted in the architecture. + + Enable this config to enable selective emulation of these + features. + + If unsure, say Y + +if ARMV8_DEPRECATED + +config SWP_EMULATION + bool "Emulate SWP/SWPB instructions" + help + ARMv8 obsoletes the use of A32 SWP/SWPB instructions such that + they are always undefined. Say Y here to enable software + emulation of these instructions for userspace using LDXR/STXR. + + In some older versions of glibc [<=2.8] SWP is used during futex + trylock() operations with the assumption that the code will not + be preempted. This invalid assumption may be more likely to fail + with SWP emulation enabled, leading to deadlock of the user + application. + + NOTE: when accessing uncached shared regions, LDXR/STXR rely + on an external transaction monitoring block called a global + monitor to maintain update atomicity. If your system does not + implement a global monitor, this option can cause programs that + perform SWP operations to uncached memory to deadlock. + + If unsure, say Y + +config CP15_BARRIER_EMULATION + bool "Emulate CP15 Barrier instructions" + help + The CP15 barrier instructions - CP15ISB, CP15DSB, and + CP15DMB - are deprecated in ARMv8 (and ARMv7). It is + strongly recommended to use the ISB, DSB, and DMB + instructions instead. + + Say Y here to enable software emulation of these + instructions for AArch32 userspace code. When this option is + enabled, CP15 barrier usage is traced which can help + identify software that needs updating. + + If unsure, say Y + +config SETEND_EMULATION + bool "Emulate SETEND instruction" + help + The SETEND instruction alters the data-endianness of the + AArch32 EL0, and is deprecated in ARMv8. + + Say Y here to enable software emulation of the instruction + for AArch32 userspace code. + + Note: All the cpus on the system must have mixed endian support at EL0 + for this feature to be enabled. If a new CPU - which doesn't support mixed + endian - is hotplugged in after this feature has been enabled, there could + be unexpected results in the applications. + + If unsure, say Y + +endif + endmenu menu "Boot options" diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h new file mode 100644 index 000000000000..056443086019 --- /dev/null +++ b/arch/arm64/include/asm/cpu.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014 ARM Ltd. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#ifndef __ASM_CPU_H +#define __ASM_CPU_H + +#include <linux/cpu.h> +#include <linux/init.h> +#include <linux/percpu.h> + +/* + * Records attributes of an individual CPU. + */ +struct cpuinfo_arm64 { + struct cpu cpu; + u32 reg_ctr; + u32 reg_cntfrq; + u32 reg_dczid; + u32 reg_midr; + + u64 reg_id_aa64isar0; + u64 reg_id_aa64isar1; + u64 reg_id_aa64mmfr0; + u64 reg_id_aa64mmfr1; + u64 reg_id_aa64pfr0; + u64 reg_id_aa64pfr1; + + u32 reg_id_isar0; + u32 reg_id_isar1; + u32 reg_id_isar2; + u32 reg_id_isar3; + u32 reg_id_isar4; + u32 reg_id_isar5; + u32 reg_id_mmfr0; + u32 reg_id_mmfr1; + u32 reg_id_mmfr2; + u32 reg_id_mmfr3; + u32 reg_id_pfr0; + u32 reg_id_pfr1; +}; + +DECLARE_PER_CPU(struct cpuinfo_arm64, cpu_data); + +void cpuinfo_store_cpu(void); +void __init cpuinfo_store_boot_cpu(void); + +#endif /* __ASM_CPU_H */ diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index cd4ac0516488..7db3c63ec2e3 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -26,4 +26,7 @@ static inline bool cpu_have_feature(unsigned int num) return elf_hwcap & (1UL << num); } +bool cpu_supports_mixed_endian_el0(void); +bool system_supports_mixed_endian_el0(void); + #endif diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h index 27f54a7cc81b..39636a426c56 100644 --- a/arch/arm64/include/asm/cputype.h +++ b/arch/arm64/include/asm/cputype.h @@ -36,15 +36,46 @@ __val; \ }) +#define MIDR_REVISION_MASK 0xf +#define MIDR_REVISION(midr) ((midr) & MIDR_REVISION_MASK) +#define MIDR_PARTNUM_SHIFT 4 +#define MIDR_PARTNUM_MASK (0xfff << MIDR_PARTNUM_SHIFT) +#define MIDR_PARTNUM(midr) \ + (((midr) & MIDR_PARTNUM_MASK) >> MIDR_PARTNUM_SHIFT) +#define MIDR_ARCHITECTURE_SHIFT 16 +#define MIDR_ARCHITECTURE_MASK (0xf << MIDR_ARCHITECTURE_SHIFT) +#define MIDR_ARCHITECTURE(midr) \ + (((midr) & MIDR_ARCHITECTURE_MASK) >> MIDR_ARCHITECTURE_SHIFT) +#define MIDR_VARIANT_SHIFT 20 +#define MIDR_VARIANT_MASK (0xf << MIDR_VARIANT_SHIFT) +#define MIDR_VARIANT(midr) \ + (((midr) & MIDR_VARIANT_MASK) >> MIDR_VARIANT_SHIFT) +#define MIDR_IMPLEMENTOR_SHIFT 24 +#define MIDR_IMPLEMENTOR_MASK (0xff << MIDR_IMPLEMENTOR_SHIFT) +#define MIDR_IMPLEMENTOR(midr) \ + (((midr) & MIDR_IMPLEMENTOR_MASK) >> MIDR_IMPLEMENTOR_SHIFT) + #define ARM_CPU_IMP_ARM 0x41 #define ARM_CPU_IMP_APM 0x50 -#define ARM_CPU_PART_AEM_V8 0xD0F0 -#define ARM_CPU_PART_FOUNDATION 0xD000 -#define ARM_CPU_PART_CORTEX_A53 0xD030 -#define ARM_CPU_PART_CORTEX_A57 0xD070 +#define ARM_CPU_PART_AEM_V8 0xD0F +#define ARM_CPU_PART_FOUNDATION 0xD00 +#define ARM_CPU_PART_CORTEX_A57 0xD07 +#define ARM_CPU_PART_CORTEX_A53 0xD03 + +#define APM_CPU_PART_POTENZA 0x000 + +#define ID_AA64MMFR0_BIGENDEL0_SHIFT 16 +#define ID_AA64MMFR0_BIGENDEL0_MASK (0xf << ID_AA64MMFR0_BIGENDEL0_SHIFT) +#define ID_AA64MMFR0_BIGENDEL0(mmfr0) \ + (((mmfr0) & ID_AA64MMFR0_BIGENDEL0_MASK) >> ID_AA64MMFR0_BIGENDEL0_SHIFT) +#define ID_AA64MMFR0_BIGEND_SHIFT 8 +#define ID_AA64MMFR0_BIGEND_MASK (0xf << ID_AA64MMFR0_BIGEND_SHIFT) +#define ID_AA64MMFR0_BIGEND(mmfr0) \ + (((mmfr0) & ID_AA64MMFR0_BIGEND_MASK) >> ID_AA64MMFR0_BIGEND_SHIFT) -#define APM_CPU_PART_POTENZA 0x0000 +#define SCTLR_EL1_CP15BEN (0x1 << 5) +#define SCTLR_EL1_SED (0x1 << 8) #ifndef __ASSEMBLY__ @@ -65,12 +96,12 @@ static inline u64 __attribute_const__ read_cpuid_mpidr(void) static inline unsigned int __attribute_const__ read_cpuid_implementor(void) { - return (read_cpuid_id() & 0xFF000000) >> 24; + return MIDR_IMPLEMENTOR(read_cpuid_id()); } static inline unsigned int __attribute_const__ read_cpuid_part_number(void) { - return (read_cpuid_id() & 0xFFF0); + return MIDR_PARTNUM(read_cpuid_id()); } static inline u32 __attribute_const__ read_cpuid_cachetype(void) @@ -78,6 +109,11 @@ static inline u32 __attribute_const__ read_cpuid_cachetype(void) return read_cpuid(CTR_EL0); } +static inline bool id_aa64mmfr0_mixed_endian_el0(u64 mmfr0) +{ + return (ID_AA64MMFR0_BIGEND(mmfr0) == 0x1) || + (ID_AA64MMFR0_BIGENDEL0(mmfr0) == 0x1); +} #endif /* __ASSEMBLY__ */ #endif diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h index 62e7b8bcd2dc..95da2f2ebc6b 100644 --- a/arch/arm64/include/asm/insn.h +++ b/arch/arm64/include/asm/insn.h @@ -110,4 +110,14 @@ int aarch64_insn_patch_text(void *addrs[], u32 insns[], int cnt); #endif /* __ASSEMBLY__ */ +bool aarch32_insn_is_wide(u32 insn); + +#define A32_RN_OFFSET 16 +#define A32_RT_OFFSET 12 +#define A32_RT2_OFFSET 0 + +u32 aarch32_insn_extract_reg_num(u32 insn, int offset); +u32 aarch32_insn_mcr_extract_opc2(u32 insn); +u32 aarch32_insn_mcr_extract_crm(u32 insn); + #endif /* __ASM_INSN_H */ diff --git a/arch/arm64/include/asm/opcodes.h b/arch/arm64/include/asm/opcodes.h index fd189a522aee..4e603ea36ad3 100644 --- a/arch/arm64/include/asm/opcodes.h +++ b/arch/arm64/include/asm/opcodes.h @@ -1,231 +1 @@ -/* - * Copied from arch/arm/include/asm/opcodes.h - * - * 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. - */ - -#ifndef __ASM_ARM_OPCODES_H -#define __ASM_ARM_OPCODES_H - -#ifndef __ASSEMBLY__ -#include <linux/linkage.h> -extern asmlinkage unsigned int arm_check_condition(u32 opcode, u64 psr); -#endif - -#define ARM_OPCODE_CONDTEST_FAIL 0 -#define ARM_OPCODE_CONDTEST_PASS 1 -#define ARM_OPCODE_CONDTEST_UNCOND 2 - - -/* - * Assembler opcode byteswap helpers. - * These are only intended for use by this header: don't use them directly, - * because they will be suboptimal in most cases. - */ -#define ___asm_opcode_swab32(x) ( \ - (((x) << 24) & 0xFF000000) \ - | (((x) << 8) & 0x00FF0000) \ - | (((x) >> 8) & 0x0000FF00) \ - | (((x) >> 24) & 0x000000FF) \ -) -#define ___asm_opcode_swab16(x) ( \ - (((x) << 8) & 0xFF00) \ - | (((x) >> 8) & 0x00FF) \ -) -#define ___asm_opcode_swahb32(x) ( \ - (((x) << 8) & 0xFF00FF00) \ - | (((x) >> 8) & 0x00FF00FF) \ -) -#define ___asm_opcode_swahw32(x) ( \ - (((x) << 16) & 0xFFFF0000) \ - | (((x) >> 16) & 0x0000FFFF) \ -) -#define ___asm_opcode_identity32(x) ((x) & 0xFFFFFFFF) -#define ___asm_opcode_identity16(x) ((x) & 0xFFFF) - - -/* - * Opcode byteswap helpers - * - * These macros help with converting instructions between a canonical integer - * format and in-memory representation, in an endianness-agnostic manner. - * - * __mem_to_opcode_*() convert from in-memory representation to canonical form. - * __opcode_to_mem_*() convert from canonical form to in-memory representation. - * - * - * Canonical instruction representation: - * - * ARM: 0xKKLLMMNN - * Thumb 16-bit: 0x0000KKLL, where KK < 0xE8 - * Thumb 32-bit: 0xKKLLMMNN, where KK >= 0xE8 - * - * There is no way to distinguish an ARM instruction in canonical representation - * from a Thumb instruction (just as these cannot be distinguished in memory). - * Where this distinction is important, it needs to be tracked separately. - * - * Note that values in the range 0x0000E800..0xE7FFFFFF intentionally do not - * represent any valid Thumb-2 instruction. For this range, - * __opcode_is_thumb32() and __opcode_is_thumb16() will both be false. - * - * The ___asm variants are intended only for use by this header, in situations - * involving inline assembler. For .S files, the normal __opcode_*() macros - * should do the right thing. - */ -#ifdef __ASSEMBLY__ - -#define ___opcode_swab32(x) ___asm_opcode_swab32(x) -#define ___opcode_swab16(x) ___asm_opcode_swab16(x) -#define ___opcode_swahb32(x) ___asm_opcode_swahb32(x) -#define ___opcode_swahw32(x) ___asm_opcode_swahw32(x) -#define ___opcode_identity32(x) ___asm_opcode_identity32(x) -#define ___opcode_identity16(x) ___asm_opcode_identity16(x) - -#else /* ! __ASSEMBLY__ */ - -#include <linux/types.h> -#include <linux/swab.h> - -#define ___opcode_swab32(x) swab32(x) -#define ___opcode_swab16(x) swab16(x) -#define ___opcode_swahb32(x) swahb32(x) -#define ___opcode_swahw32(x) swahw32(x) -#define ___opcode_identity32(x) ((u32)(x)) -#define ___opcode_identity16(x) ((u16)(x)) - -#endif /* ! __ASSEMBLY__ */ - - -#ifdef CONFIG_CPU_ENDIAN_BE8 - -#define __opcode_to_mem_arm(x) ___opcode_swab32(x) -#define __opcode_to_mem_thumb16(x) ___opcode_swab16(x) -#define __opcode_to_mem_thumb32(x) ___opcode_swahb32(x) -#define ___asm_opcode_to_mem_arm(x) ___asm_opcode_swab32(x) -#define ___asm_opcode_to_mem_thumb16(x) ___asm_opcode_swab16(x) -#define ___asm_opcode_to_mem_thumb32(x) ___asm_opcode_swahb32(x) - -#else /* ! CONFIG_CPU_ENDIAN_BE8 */ - -#define __opcode_to_mem_arm(x) ___opcode_identity32(x) -#define __opcode_to_mem_thumb16(x) ___opcode_identity16(x) -#define ___asm_opcode_to_mem_arm(x) ___asm_opcode_identity32(x) -#define ___asm_opcode_to_mem_thumb16(x) ___asm_opcode_identity16(x) -#ifndef CONFIG_CPU_ENDIAN_BE32 -/* - * On BE32 systems, using 32-bit accesses to store Thumb instructions will not - * work in all cases, due to alignment constraints. For now, a correct - * version is not provided for BE32. - */ -#define __opcode_to_mem_thumb32(x) ___opcode_swahw32(x) -#define ___asm_opcode_to_mem_thumb32(x) ___asm_opcode_swahw32(x) -#endif - -#endif /* ! CONFIG_CPU_ENDIAN_BE8 */ - -#define __mem_to_opcode_arm(x) __opcode_to_mem_arm(x) -#define __mem_to_opcode_thumb16(x) __opcode_to_mem_thumb16(x) -#ifndef CONFIG_CPU_ENDIAN_BE32 -#define __mem_to_opcode_thumb32(x) __opcode_to_mem_thumb32(x) -#endif - -/* Operations specific to Thumb opcodes */ - -/* Instruction size checks: */ -#define __opcode_is_thumb32(x) ( \ - ((x) & 0xF8000000) == 0xE8000000 \ - || ((x) & 0xF0000000) == 0xF0000000 \ -) -#define __opcode_is_thumb16(x) ( \ - ((x) & 0xFFFF0000) == 0 \ - && !(((x) & 0xF800) == 0xE800 || ((x) & 0xF000) == 0xF000) \ -) - -/* Operations to construct or split 32-bit Thumb instructions: */ -#define __opcode_thumb32_first(x) (___opcode_identity16((x) >> 16)) -#define __opcode_thumb32_second(x) (___opcode_identity16(x)) -#define __opcode_thumb32_compose(first, second) ( \ - (___opcode_identity32(___opcode_identity16(first)) << 16) \ - | ___opcode_identity32(___opcode_identity16(second)) \ -) -#define ___asm_opcode_thumb32_first(x) (___asm_opcode_identity16((x) >> 16)) -#define ___asm_opcode_thumb32_second(x) (___asm_opcode_identity16(x)) -#define ___asm_opcode_thumb32_compose(first, second) ( \ - (___asm_opcode_identity32(___asm_opcode_identity16(first)) << 16) \ - | ___asm_opcode_identity32(___asm_opcode_identity16(second)) \ -) - -/* - * Opcode injection helpers - * - * In rare cases it is necessary to assemble an opcode which the - * assembler does not support directly, or which would normally be - * rejected because of the CFLAGS or AFLAGS used to build the affected - * file. - * - * Before using these macros, consider carefully whether it is feasible - * instead to change the build flags for your file, or whether it really - * makes sense to support old assembler versions when building that - * particular kernel feature. - * - * The macros defined here should only be used where there is no viable - * alternative. - * - * - * __inst_arm(x): emit the specified ARM opcode - * __inst_thumb16(x): emit the specified 16-bit Thumb opcode - * __inst_thumb32(x): emit the specified 32-bit Thumb opcode - * - * __inst_arm_thumb16(arm, thumb): emit either the specified arm or - * 16-bit Thumb opcode, depending on whether an ARM or Thumb-2 - * kernel is being built - * - * __inst_arm_thumb32(arm, thumb): emit either the specified arm or - * 32-bit Thumb opcode, depending on whether an ARM or Thumb-2 - * kernel is being built - * - * - * Note that using these macros directly is poor practice. Instead, you - * should use them to define human-readable wrapper macros to encode the - * instructions that you care about. In code which might run on ARMv7 or - * above, you can usually use the __inst_arm_thumb{16,32} macros to - * specify the ARM and Thumb alternatives at the same time. This ensures - * that the correct opcode gets emitted depending on the instruction set - * used for the kernel build. - * - * Look at opcodes-virt.h for an example of how to use these macros. - */ -#include <linux/stringify.h> - -#define __inst_arm(x) ___inst_arm(___asm_opcode_to_mem_arm(x)) -#define __inst_thumb32(x) ___inst_thumb32( \ - ___asm_opcode_to_mem_thumb16(___asm_opcode_thumb32_first(x)), \ - ___asm_opcode_to_mem_thumb16(___asm_opcode_thumb32_second(x)) \ -) -#define __inst_thumb16(x) ___inst_thumb16(___asm_opcode_to_mem_thumb16(x)) - -#ifdef CONFIG_THUMB2_KERNEL -#define __inst_arm_thumb16(arm_opcode, thumb_opcode) \ - __inst_thumb16(thumb_opcode) -#define __inst_arm_thumb32(arm_opcode, thumb_opcode) \ - __inst_thumb32(thumb_opcode) -#else -#define __inst_arm_thumb16(arm_opcode, thumb_opcode) __inst_arm(arm_opcode) -#define __inst_arm_thumb32(arm_opcode, thumb_opcode) __inst_arm(arm_opcode) -#endif - -/* Helpers for the helpers. Don't use these directly. */ -#ifdef __ASSEMBLY__ -#define ___inst_arm(x) .long x -#define ___inst_thumb16(x) .short x -#define ___inst_thumb32(first, second) .short first, second -#else -#define ___inst_arm(x) ".long " __stringify(x) "\n\t" -#define ___inst_thumb16(x) ".short " __stringify(x) "\n\t" -#define ___inst_thumb32(first, second) \ - ".short " __stringify(first) ", " __stringify(second) "\n\t" -#endif - -#endif /* __ASM_ARM_OPCODES_H */ +#include <../../arm/include/asm/opcodes.h> diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h index c29d88526405..904d936bbab5 100644 --- a/arch/arm64/include/asm/ptrace.h +++ b/arch/arm64/include/asm/ptrace.h @@ -54,6 +54,13 @@ #define COMPAT_PSR_Z_BIT 0x40000000 #define COMPAT_PSR_N_BIT 0x80000000 #define COMPAT_PSR_IT_MASK 0x0600fc00 /* If-Then execution state mask */ + +#ifdef CONFIG_CPU_BIG_ENDIAN +#define COMPAT_PSR_ENDSTATE COMPAT_PSR_E_BIT +#else +#define COMPAT_PSR_ENDSTATE 0 +#endif + /* * These are 'magic' values for PTRACE_PEEKUSR that return info about where a * process is located in memory. @@ -187,13 +194,5 @@ extern unsigned long profile_pc(struct pt_regs *regs); #define profile_pc(regs) instruction_pointer(regs) #endif -/* - * True if instr is a 32-bit thumb instruction. This works if instr - * is the first or only half-word of a thumb instruction. It also works - * when instr holds all 32-bits of a wide thumb instruction if stored - * in the form (first_half<<16)|(second_half) - */ -#define is_wide_instruction(instr) ((unsigned)(instr) >= 0xe800) - #endif /* __ASSEMBLY__ */ #endif diff --git a/arch/arm64/include/asm/traps.h b/arch/arm64/include/asm/traps.h index 75f51eadb9eb..232e4ba5d314 100644 --- a/arch/arm64/include/asm/traps.h +++ b/arch/arm64/include/asm/traps.h @@ -20,16 +20,19 @@ #include <linux/list.h> +struct pt_regs; + struct undef_hook { struct list_head node; u32 instr_mask; u32 instr_val; - u32 pstate_mask; - u32 pstate_val; - int (*fn)(struct pt_regs *regs, unsigned int instr); + u64 pstate_mask; + u64 pstate_val; + int (*fn)(struct pt_regs *regs, u32 instr); }; void register_undef_hook(struct undef_hook *hook); +void unregister_undef_hook(struct undef_hook *hook); static inline int in_exception_text(unsigned long ptr) { diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 09914fa89c69..5e0cb2d2f133 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -10,20 +10,21 @@ CFLAGS_efi-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) \ CFLAGS_REMOVE_ftrace.o = -pg CFLAGS_REMOVE_insn.o = -pg CFLAGS_REMOVE_return_address.o = -pg +CFLAGS_armv8_deprecated.o := -I$(src) # Object file lists. arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ sys.o stacktrace.o time.o traps.o io.o vdso.o \ hyp-stub.o psci.o cpu_ops.o insn.o return_address.o \ - opcodes.o + cpuinfo.o -arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ - sys_compat.o +arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ + sys_compat.o \ + ../../arm/kernel/opcodes.o arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o topology.o -arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o arm64-obj-$(CONFIG_PERF_EVENTS) += perf_regs.o arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o @@ -32,8 +33,7 @@ arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o arm64-obj-$(CONFIG_KGDB) += kgdb.o arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o - -obj-$(CONFIG_SWP_EMULATE) += swp_emulate.o +arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o obj-y += $(arm64-obj-y) vdso/ obj-m += $(arm64-obj-m) diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c new file mode 100644 index 000000000000..294db9f07bbd --- /dev/null +++ b/arch/arm64/kernel/armv8_deprecated.c @@ -0,0 +1,663 @@ +/* + * Copyright (C) 2014 ARM Limited + * + * 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/cpu.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/perf_event.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/sysctl.h> + +#include <asm/cputype.h> +#include <asm/insn.h> +#include <asm/opcodes.h> +#include <asm/system_misc.h> +#include <asm/traps.h> +#include <asm/uaccess.h> +#include <asm/cpufeature.h> + +#define CREATE_TRACE_POINTS +#include "trace-events-emulation.h" + +/* + * The runtime support for deprecated instruction support can be in one of + * following three states - + * + * 0 = undef + * 1 = emulate (software emulation) + * 2 = hw (supported in hardware) + */ +enum insn_emulation_mode { + INSN_UNDEF, + INSN_EMULATE, + INSN_HW, +}; + +enum legacy_insn_status { + INSN_DEPRECATED, + INSN_OBSOLETE, +}; + +struct insn_emulation_ops { + const char *name; + enum legacy_insn_status status; + struct undef_hook *hooks; + int (*set_hw_mode)(bool enable); +}; + +struct insn_emulation { + struct list_head node; + struct insn_emulation_ops *ops; + int current_mode; + int min; + int max; +}; + +static LIST_HEAD(insn_emulation); +static int nr_insn_emulated; +static DEFINE_RAW_SPINLOCK(insn_emulation_lock); + +static void register_emulation_hooks(struct insn_emulation_ops *ops) +{ + struct undef_hook *hook; + + BUG_ON(!ops->hooks); + + for (hook = ops->hooks; hook->instr_mask; hook++) + register_undef_hook(hook); + + pr_notice("Registered %s emulation handler\n", ops->name); +} + +static void remove_emulation_hooks(struct insn_emulation_ops *ops) +{ + struct undef_hook *hook; + + BUG_ON(!ops->hooks); + + for (hook = ops->hooks; hook->instr_mask; hook++) + unregister_undef_hook(hook); + + pr_notice("Removed %s emulation handler\n", ops->name); +} + +static void enable_insn_hw_mode(void *data) +{ + struct insn_emulation *insn = (struct insn_emulation *)data; + if (insn->ops->set_hw_mode) + insn->ops->set_hw_mode(true); +} + +static void disable_insn_hw_mode(void *data) +{ + struct insn_emulation *insn = (struct insn_emulation *)data; + if (insn->ops->set_hw_mode) + insn->ops->set_hw_mode(false); +} + +/* Run set_hw_mode(mode) on all active CPUs */ +static int run_all_cpu_set_hw_mode(struct insn_emulation *insn, bool enable) +{ + if (!insn->ops->set_hw_mode) + return -EINVAL; + if (enable) + on_each_cpu(enable_insn_hw_mode, (void *)insn, true); + else + on_each_cpu(disable_insn_hw_mode, (void *)insn, true); + return 0; +} + +/* + * Run set_hw_mode for all insns on a starting CPU. + * Returns: + * 0 - If all the hooks ran successfully. + * -EINVAL - At least one hook is not supported by the CPU. + */ +static int run_all_insn_set_hw_mode(unsigned long cpu) +{ + int rc = 0; + unsigned long flags; + struct insn_emulation *insn; + + raw_spin_lock_irqsave(&insn_emulation_lock, flags); + list_for_each_entry(insn, &insn_emulation, node) { + bool enable = (insn->current_mode == INSN_HW); + if (insn->ops->set_hw_mode && insn->ops->set_hw_mode(enable)) { + pr_warn("CPU[%ld] cannot support the emulation of %s", + cpu, insn->ops->name); + rc = -EINVAL; + } + } + raw_spin_unlock_irqrestore(&insn_emulation_lock, flags); + return rc; +} + +static int update_insn_emulation_mode(struct insn_emulation *insn, + enum insn_emulation_mode prev) +{ + int ret = 0; + + switch (prev) { + case INSN_UNDEF: /* Nothing to be done */ + break; + case INSN_EMULATE: + remove_emulation_hooks(insn->ops); + break; + case INSN_HW: + if (!run_all_cpu_set_hw_mode(insn, false)) + pr_notice("Disabled %s support\n", insn->ops->name); + break; + } + + switch (insn->current_mode) { + case INSN_UNDEF: + break; + case INSN_EMULATE: + register_emulation_hooks(insn->ops); + break; + case INSN_HW: + ret = run_all_cpu_set_hw_mode(insn, true); + if (!ret) + pr_notice("Enabled %s support\n", insn->ops->name); + break; + } + + return ret; +} + +static void register_insn_emulation(struct insn_emulation_ops *ops) +{ + unsigned long flags; + struct insn_emulation *insn; + + insn = kzalloc(sizeof(*insn), GFP_KERNEL); + insn->ops = ops; + insn->min = INSN_UNDEF; + + switch (ops->status) { + case INSN_DEPRECATED: + insn->current_mode = INSN_EMULATE; + /* Disable the HW mode if it was turned on at early boot time */ + run_all_cpu_set_hw_mode(insn, false); + insn->max = INSN_HW; + break; + case INSN_OBSOLETE: + insn->current_mode = INSN_UNDEF; + insn->max = INSN_EMULATE; + break; + } + + raw_spin_lock_irqsave(&insn_emulation_lock, flags); + list_add(&insn->node, &insn_emulation); + nr_insn_emulated++; + raw_spin_unlock_irqrestore(&insn_emulation_lock, flags); + + /* Register any handlers if required */ + update_insn_emulation_mode(insn, INSN_UNDEF); +} + +static int emulation_proc_handler(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + int ret = 0; + struct insn_emulation *insn = (struct insn_emulation *) table->data; + enum insn_emulation_mode prev_mode = insn->current_mode; + + table->data = &insn->current_mode; + ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); + + if (ret || !write || prev_mode == insn->current_mode) + goto ret; + + ret = update_insn_emulation_mode(insn, prev_mode); + if (ret) { + /* Mode change failed, revert to previous mode. */ + insn->current_mode = prev_mode; + update_insn_emulation_mode(insn, INSN_UNDEF); + } +ret: + table->data = insn; + return ret; +} + +static struct ctl_table ctl_abi[] = { + { + .procname = "abi", + .mode = 0555, + }, + { } +}; + +static void register_insn_emulation_sysctl(struct ctl_table *table) +{ + unsigned long flags; + int i = 0; + struct insn_emulation *insn; + struct ctl_table *insns_sysctl, *sysctl; + + insns_sysctl = kzalloc(sizeof(*sysctl) * (nr_insn_emulated + 1), + GFP_KERNEL); + + raw_spin_lock_irqsave(&insn_emulation_lock, flags); + list_for_each_entry(insn, &insn_emulation, node) { + sysctl = &insns_sysctl[i]; + + sysctl->mode = 0644; + sysctl->maxlen = sizeof(int); + + sysctl->procname = insn->ops->name; + sysctl->data = insn; + sysctl->extra1 = &insn->min; + sysctl->extra2 = &insn->max; + sysctl->proc_handler = emulation_proc_handler; + i++; + } + raw_spin_unlock_irqrestore(&insn_emulation_lock, flags); + + table->child = insns_sysctl; + register_sysctl_table(table); +} + +/* + * Implement emulation of the SWP/SWPB instructions using load-exclusive and + * store-exclusive. + * + * Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>] + * Where: Rt = destination + * Rt2 = source + * Rn = address + */ + +/* + * Error-checking SWP macros implemented using ldxr{b}/stxr{b} + */ +#define __user_swpX_asm(data, addr, res, temp, B) \ + __asm__ __volatile__( \ + " mov %w2, %w1\n" \ + "0: ldxr"B" %w1, [%3]\n" \ + "1: stxr"B" %w0, %w2, [%3]\n" \ + " cbz %w0, 2f\n" \ + " mov %w0, %w4\n" \ + "2:\n" \ + " .pushsection .fixup,\"ax\"\n" \ + " .align 2\n" \ + "3: mov %w0, %w5\n" \ + " b 2b\n" \ + " .popsection" \ + " .pushsection __ex_table,\"a\"\n" \ + " .align 3\n" \ + " .quad 0b, 3b\n" \ + " .quad 1b, 3b\n" \ + " .popsection" \ + : "=&r" (res), "+r" (data), "=&r" (temp) \ + : "r" (addr), "i" (-EAGAIN), "i" (-EFAULT) \ + : "memory") + +#define __user_swp_asm(data, addr, res, temp) \ + __user_swpX_asm(data, addr, res, temp, "") +#define __user_swpb_asm(data, addr, res, temp) \ + __user_swpX_asm(data, addr, res, temp, "b") + +/* + * Bit 22 of the instruction encoding distinguishes between + * the SWP and SWPB variants (bit set means SWPB). + */ +#define TYPE_SWPB (1 << 22) + +/* + * Set up process info to signal segmentation fault - called on access error. + */ +static void set_segfault(struct pt_regs *regs, unsigned long addr) +{ + siginfo_t info; + + down_read(¤t->mm->mmap_sem); + if (find_vma(current->mm, addr) == NULL) + info.si_code = SEGV_MAPERR; + else + info.si_code = SEGV_ACCERR; + up_read(¤t->mm->mmap_sem); + + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_addr = (void *) instruction_pointer(regs); + + pr_debug("SWP{B} emulation: access caused memory abort!\n"); + arm64_notify_die("Illegal memory access", regs, &info, 0); +} + +static int emulate_swpX(unsigned int address, unsigned int *data, + unsigned int type) +{ + unsigned int res = 0; + + if ((type != TYPE_SWPB) && (address & 0x3)) { + /* SWP to unaligned address not permitted */ + pr_debug("SWP instruction on unaligned pointer!\n"); + return -EFAULT; + } + + while (1) { + unsigned long temp; + + if (type == TYPE_SWPB) + __user_swpb_asm(*data, address, res, temp); + else + __user_swp_asm(*data, address, res, temp); + + if (likely(res != -EAGAIN) || signal_pending(current)) + break; + + cond_resched(); + } + + return res; +} + +/* + * swp_handler logs the id of calling process, dissects the instruction, sanity + * checks the memory location, calls emulate_swpX for the actual operation and + * deals with fixup/error handling before returning + */ +static int swp_handler(struct pt_regs *regs, u32 instr) +{ + u32 destreg, data, type, address = 0; + int rn, rt2, res = 0; + + perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc); + + type = instr & TYPE_SWPB; + + switch (arm_check_condition(instr, regs->pstate)) { + case ARM_OPCODE_CONDTEST_PASS: + break; + case ARM_OPCODE_CONDTEST_FAIL: + /* Condition failed - return to next instruction */ + goto ret; + case ARM_OPCODE_CONDTEST_UNCOND: + /* If unconditional encoding - not a SWP, undef */ + return -EFAULT; + default: + return -EINVAL; + } + + rn = aarch32_insn_extract_reg_num(instr, A32_RN_OFFSET); + rt2 = aarch32_insn_extract_reg_num(instr, A32_RT2_OFFSET); + + address = (u32)regs->user_regs.regs[rn]; + data = (u32)regs->user_regs.regs[rt2]; + destreg = aarch32_insn_extract_reg_num(instr, A32_RT_OFFSET); + + pr_debug("addr in r%d->0x%08x, dest is r%d, source in r%d->0x%08x)\n", + rn, address, destreg, + aarch32_insn_extract_reg_num(instr, A32_RT2_OFFSET), data); + + /* Check access in reasonable access range for both SWP and SWPB */ + if (!access_ok(VERIFY_WRITE, (address & ~3), 4)) { + pr_debug("SWP{B} emulation: access to 0x%08x not allowed!\n", + address); + goto fault; + } + + res = emulate_swpX(address, &data, type); + if (res == -EFAULT) + goto fault; + else if (res == 0) + regs->user_regs.regs[destreg] = data; + +ret: + if (type == TYPE_SWPB) + trace_instruction_emulation("swpb", regs->pc); + else + trace_instruction_emulation("swp", regs->pc); + + pr_warn_ratelimited("\"%s\" (%ld) uses obsolete SWP{B} instruction at 0x%llx\n", + current->comm, (unsigned long)current->pid, regs->pc); + + regs->pc += 4; + return 0; + +fault: + set_segfault(regs, address); + + return 0; +} + +/* + * Only emulate SWP/SWPB executed in ARM state/User mode. + * The kernel must be SWP free and SWP{B} does not exist in Thumb. + */ +static struct undef_hook swp_hooks[] = { + { + .instr_mask = 0x0fb00ff0, + .instr_val = 0x01000090, + .pstate_mask = COMPAT_PSR_MODE_MASK, + .pstate_val = COMPAT_PSR_MODE_USR, + .fn = swp_handler + }, + { } +}; + +static struct insn_emulation_ops swp_ops = { + .name = "swp", + .status = INSN_OBSOLETE, + .hooks = swp_hooks, + .set_hw_mode = NULL, +}; + +static int cp15barrier_handler(struct pt_regs *regs, u32 instr) +{ + perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc); + + switch (arm_check_condition(instr, regs->pstate)) { + case ARM_OPCODE_CONDTEST_PASS: + break; + case ARM_OPCODE_CONDTEST_FAIL: + /* Condition failed - return to next instruction */ + goto ret; + case ARM_OPCODE_CONDTEST_UNCOND: + /* If unconditional encoding - not a barrier instruction */ + return -EFAULT; + default: + return -EINVAL; + } + + switch (aarch32_insn_mcr_extract_crm(instr)) { + case 10: + /* + * dmb - mcr p15, 0, Rt, c7, c10, 5 + * dsb - mcr p15, 0, Rt, c7, c10, 4 + */ + if (aarch32_insn_mcr_extract_opc2(instr) == 5) { + dmb(sy); + trace_instruction_emulation( + "mcr p15, 0, Rt, c7, c10, 5 ; dmb", regs->pc); + } else { + dsb(sy); + trace_instruction_emulation( + "mcr p15, 0, Rt, c7, c10, 4 ; dsb", regs->pc); + } + break; + case 5: + /* + * isb - mcr p15, 0, Rt, c7, c5, 4 + * + * Taking an exception or returning from one acts as an + * instruction barrier. So no explicit barrier needed here. + */ + trace_instruction_emulation( + "mcr p15, 0, Rt, c7, c5, 4 ; isb", regs->pc); + break; + } + +ret: + pr_warn_ratelimited("\"%s\" (%ld) uses deprecated CP15 Barrier instruction at 0x%llx\n", + current->comm, (unsigned long)current->pid, regs->pc); + + regs->pc += 4; + return 0; +} + +static inline void config_sctlr_el1(u32 clear, u32 set) +{ + u32 val; + + asm volatile("mrs %0, sctlr_el1" : "=r" (val)); + val &= ~clear; + val |= set; + asm volatile("msr sctlr_el1, %0" : : "r" (val)); +} + +static int cp15_barrier_set_hw_mode(bool enable) +{ + if (enable) + config_sctlr_el1(0, SCTLR_EL1_CP15BEN); + else + config_sctlr_el1(SCTLR_EL1_CP15BEN, 0); + return 0; +} + +static struct undef_hook cp15_barrier_hooks[] = { + { + .instr_mask = 0x0fff0fdf, + .instr_val = 0x0e070f9a, + .pstate_mask = COMPAT_PSR_MODE_MASK, + .pstate_val = COMPAT_PSR_MODE_USR, + .fn = cp15barrier_handler, + }, + { + .instr_mask = 0x0fff0fff, + .instr_val = 0x0e070f95, + .pstate_mask = COMPAT_PSR_MODE_MASK, + .pstate_val = COMPAT_PSR_MODE_USR, + .fn = cp15barrier_handler, + }, + { } +}; + +static struct insn_emulation_ops cp15_barrier_ops = { + .name = "cp15_barrier", + .status = INSN_DEPRECATED, + .hooks = cp15_barrier_hooks, + .set_hw_mode = cp15_barrier_set_hw_mode, +}; + +static int setend_set_hw_mode(bool enable) +{ + if (!cpu_supports_mixed_endian_el0()) + return -EINVAL; + + if (enable) + config_sctlr_el1(SCTLR_EL1_SED, 0); + else + config_sctlr_el1(0, SCTLR_EL1_SED); + return 0; +} + +static int compat_setend_handler(struct pt_regs *regs, u32 big_endian) +{ + char *insn; + + perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc); + + if (big_endian) { + insn = "setend be"; + regs->pstate |= COMPAT_PSR_E_BIT; + } else { + insn = "setend le"; + regs->pstate &= ~COMPAT_PSR_E_BIT; + } + + trace_instruction_emulation(insn, regs->pc); + pr_warn_ratelimited("\"%s\" (%ld) uses deprecated setend instruction at 0x%llx\n", + current->comm, (unsigned long)current->pid, regs->pc); + + return 0; +} + +static int a32_setend_handler(struct pt_regs *regs, u32 instr) +{ + int rc = compat_setend_handler(regs, (instr >> 9) & 1); + regs->pc += 4; + return rc; +} + +static int t16_setend_handler(struct pt_regs *regs, u32 instr) +{ + int rc = compat_setend_handler(regs, (instr >> 3) & 1); + regs->pc += 2; + return rc; +} + +static struct undef_hook setend_hooks[] = { + { + .instr_mask = 0xfffffdff, + .instr_val = 0xf1010000, + .pstate_mask = COMPAT_PSR_MODE_MASK, + .pstate_val = COMPAT_PSR_MODE_USR, + .fn = a32_setend_handler, + }, + { + /* Thumb mode */ + .instr_mask = 0x0000fff7, + .instr_val = 0x0000b650, + .pstate_mask = (COMPAT_PSR_T_BIT | COMPAT_PSR_MODE_MASK), + .pstate_val = (COMPAT_PSR_T_BIT | COMPAT_PSR_MODE_USR), + .fn = t16_setend_handler, + }, + {} +}; + +static struct insn_emulation_ops setend_ops = { + .name = "setend", + .status = INSN_DEPRECATED, + .hooks = setend_hooks, + .set_hw_mode = setend_set_hw_mode, +}; + +static int insn_cpu_hotplug_notify(struct notifier_block *b, + unsigned long action, void *hcpu) +{ + int rc = 0; + if ((action & ~CPU_TASKS_FROZEN) == CPU_STARTING) + rc = run_all_insn_set_hw_mode((unsigned long)hcpu); + + return notifier_from_errno(rc); +} + +static struct notifier_block insn_cpu_hotplug_notifier = { + .notifier_call = insn_cpu_hotplug_notify, +}; + +/* + * Invoked as late_initcall, since not needed before init spawned. + */ +static int __init armv8_deprecated_init(void) +{ + if (IS_ENABLED(CONFIG_SWP_EMULATION)) + register_insn_emulation(&swp_ops); + + if (IS_ENABLED(CONFIG_CP15_BARRIER_EMULATION)) + register_insn_emulation(&cp15_barrier_ops); + + if (IS_ENABLED(CONFIG_SETEND_EMULATION)) { + if(system_supports_mixed_endian_el0()) + register_insn_emulation(&setend_ops); + else + pr_info("setend instruction emulation is not supported on the system"); + } + + register_cpu_notifier(&insn_cpu_hotplug_notifier); + register_insn_emulation_sysctl(ctl_abi); + + return 0; +} + +late_initcall(armv8_deprecated_init); diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c new file mode 100644 index 000000000000..c7fd0b9bb64d --- /dev/null +++ b/arch/arm64/kernel/cpuinfo.c @@ -0,0 +1,95 @@ +/* + * Record and handle CPU attributes. + * + * Copyright (C) 2014 ARM Ltd. + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include <asm/arch_timer.h> +#include <asm/cachetype.h> +#include <asm/cpu.h> +#include <asm/cputype.h> + +#include <linux/init.h> +#include <linux/smp.h> + +/* + * In case the boot CPU is hotpluggable, we record its initial state and + * current state separately. Certain system registers may contain different + * values depending on configuration at or after reset. + */ +DEFINE_PER_CPU(struct cpuinfo_arm64, cpu_data); +static struct cpuinfo_arm64 boot_cpu_data; +static bool mixed_endian_el0 = true; + +bool cpu_supports_mixed_endian_el0(void) +{ + return id_aa64mmfr0_mixed_endian_el0(read_cpuid(ID_AA64MMFR0_EL1)); +} + +bool system_supports_mixed_endian_el0(void) +{ + return mixed_endian_el0; +} + +static void update_mixed_endian_el0_support(struct cpuinfo_arm64 *info) +{ + mixed_endian_el0 &= id_aa64mmfr0_mixed_endian_el0(info->reg_id_aa64mmfr0); +} + +static void update_cpu_features(struct cpuinfo_arm64 *info) +{ + update_mixed_endian_el0_support(info); +} + +static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info) +{ + info->reg_cntfrq = arch_timer_get_cntfrq(); + info->reg_ctr = read_cpuid_cachetype(); + info->reg_dczid = read_cpuid(DCZID_EL0); + info->reg_midr = read_cpuid_id(); + + info->reg_id_aa64isar0 = read_cpuid(ID_AA64ISAR0_EL1); + info->reg_id_aa64isar1 = read_cpuid(ID_AA64ISAR1_EL1); + info->reg_id_aa64mmfr0 = read_cpuid(ID_AA64MMFR0_EL1); + info->reg_id_aa64mmfr1 = read_cpuid(ID_AA64MMFR1_EL1); + info->reg_id_aa64pfr0 = read_cpuid(ID_AA64PFR0_EL1); + info->reg_id_aa64pfr1 = read_cpuid(ID_AA64PFR1_EL1); + + info->reg_id_isar0 = read_cpuid(ID_ISAR0_EL1); + info->reg_id_isar1 = read_cpuid(ID_ISAR1_EL1); + info->reg_id_isar2 = read_cpuid(ID_ISAR2_EL1); + info->reg_id_isar3 = read_cpuid(ID_ISAR3_EL1); + info->reg_id_isar4 = read_cpuid(ID_ISAR4_EL1); + info->reg_id_isar5 = read_cpuid(ID_ISAR5_EL1); + info->reg_id_mmfr0 = read_cpuid(ID_MMFR0_EL1); + info->reg_id_mmfr1 = read_cpuid(ID_MMFR1_EL1); + info->reg_id_mmfr2 = read_cpuid(ID_MMFR2_EL1); + info->reg_id_mmfr3 = read_cpuid(ID_MMFR3_EL1); + info->reg_id_pfr0 = read_cpuid(ID_PFR0_EL1); + info->reg_id_pfr1 = read_cpuid(ID_PFR1_EL1); + update_cpu_features(info); +} + +void cpuinfo_store_cpu(void) +{ + struct cpuinfo_arm64 *info = this_cpu_ptr(&cpu_data); + __cpuinfo_store_cpu(info); +} + +void __init cpuinfo_store_boot_cpu(void) +{ + struct cpuinfo_arm64 *info = &per_cpu(cpu_data, 0); + __cpuinfo_store_cpu(info); + + boot_cpu_data = *info; +} diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c index 565e26f23f31..84fb4ead1cf1 100644 --- a/arch/arm64/kernel/insn.c +++ b/arch/arm64/kernel/insn.c @@ -303,3 +303,29 @@ u32 __kprobes aarch64_insn_gen_nop(void) { return aarch64_insn_gen_hint(AARCH64_INSN_HINT_NOP); } + +bool aarch32_insn_is_wide(u32 insn) +{ + return insn >= 0xe800; +} + +/* + * Macros/defines for extracting register numbers from instruction. + */ +u32 aarch32_insn_extract_reg_num(u32 insn, int offset) +{ + return (insn & (0xf << offset)) >> offset; +} + +#define OPC2_MASK 0x7 +#define OPC2_OFFSET 5 +u32 aarch32_insn_mcr_extract_opc2(u32 insn) +{ + return (insn & (OPC2_MASK << OPC2_OFFSET)) >> OPC2_OFFSET; +} + +#define CRM_MASK 0xf +u32 aarch32_insn_mcr_extract_crm(u32 insn) +{ + return insn & CRM_MASK; +} diff --git a/arch/arm64/kernel/opcodes.c b/arch/arm64/kernel/opcodes.c deleted file mode 100644 index ceb5a04a1e12..000000000000 --- a/arch/arm64/kernel/opcodes.c +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copied from linux/arch/arm/kernel/opcodes.c - * - * A32 condition code lookup feature moved from nwfpe/fpopcode.c - * - * 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/module.h> -#include <asm/opcodes.h> - -#define ARM_OPCODE_CONDITION_UNCOND 0xf - -/* - * condition code lookup table - * index into the table is test code: EQ, NE, ... LT, GT, AL, NV - * - * bit position in short is condition code: NZCV - */ -static const unsigned short cc_map[16] = { - 0xF0F0, /* EQ == Z set */ - 0x0F0F, /* NE */ - 0xCCCC, /* CS == C set */ - 0x3333, /* CC */ - 0xFF00, /* MI == N set */ - 0x00FF, /* PL */ - 0xAAAA, /* VS == V set */ - 0x5555, /* VC */ - 0x0C0C, /* HI == C set && Z clear */ - 0xF3F3, /* LS == C clear || Z set */ - 0xAA55, /* GE == (N==V) */ - 0x55AA, /* LT == (N!=V) */ - 0x0A05, /* GT == (!Z && (N==V)) */ - 0xF5FA, /* LE == (Z || (N!=V)) */ - 0xFFFF, /* AL always */ - 0 /* NV */ -}; - -/* - * Returns: - * ARM_OPCODE_CONDTEST_FAIL - if condition fails - * ARM_OPCODE_CONDTEST_PASS - if condition passes (including AL) - * ARM_OPCODE_CONDTEST_UNCOND - if NV condition, or separate unconditional - * opcode space from v5 onwards - * - * Code that tests whether a conditional instruction would pass its condition - * check should check that return value == ARM_OPCODE_CONDTEST_PASS. - * - * Code that tests if a condition means that the instruction would be executed - * (regardless of conditional or unconditional) should instead check that the - * return value != ARM_OPCODE_CONDTEST_FAIL. - */ -asmlinkage unsigned int arm_check_condition(u32 opcode, u64 psr) -{ - u32 cc_bits = opcode >> 28; - u32 psr_cond = (u32)(psr & 0xffffffff) >> 28; - unsigned int ret; - - if (cc_bits != ARM_OPCODE_CONDITION_UNCOND) { - if ((cc_map[cc_bits] >> (psr_cond)) & 1) - ret = ARM_OPCODE_CONDTEST_PASS; - else - ret = ARM_OPCODE_CONDTEST_FAIL; - } else { - ret = ARM_OPCODE_CONDTEST_UNCOND; - } - - return ret; -} -EXPORT_SYMBOL_GPL(arm_check_condition); diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 5094b1c5e470..8290d06ef188 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -42,8 +42,10 @@ #include <linux/of_fdt.h> #include <linux/of_platform.h> #include <linux/efi.h> +#include <linux/personality.h> #include <asm/fixmap.h> +#include <asm/cpu.h> #include <asm/cputype.h> #include <asm/elf.h> #include <asm/cputable.h> @@ -77,7 +79,6 @@ unsigned int compat_elf_hwcap2 __read_mostly; #endif static const char *cpu_name; -static const char *machine_name; phys_addr_t __fdt_pointer __initdata; /* @@ -217,6 +218,8 @@ static void __init setup_processor(void) sprintf(init_utsname()->machine, ELF_PLATFORM); elf_hwcap = 0; + cpuinfo_store_boot_cpu(); + /* * ID_AA64ISAR0_EL1 contains 4-bit wide signed feature blocks. * The blocks we test below represent incremental functionality @@ -294,7 +297,7 @@ static void __init setup_machine_fdt(phys_addr_t dt_phys) cpu_relax(); } - machine_name = of_flat_dt_get_machine_name(); + dump_stack_set_arch_desc("%s (DT)", of_flat_dt_get_machine_name()); } /* @@ -404,14 +407,12 @@ static int __init arm64_device_init(void) } arch_initcall_sync(arm64_device_init); -static DEFINE_PER_CPU(struct cpu, cpu_data); - static int __init topology_init(void) { int i; for_each_possible_cpu(i) { - struct cpu *cpu = &per_cpu(cpu_data, i); + struct cpu *cpu = &per_cpu(cpu_data.cpu, i); cpu->hotpluggable = 1; register_cpu(cpu, i); } @@ -432,14 +433,50 @@ static const char *hwcap_str[] = { NULL }; +#ifdef CONFIG_COMPAT +static const char *compat_hwcap_str[] = { + "swp", + "half", + "thumb", + "26bit", + "fastmult", + "fpa", + "vfp", + "edsp", + "java", + "iwmmxt", + "crunch", + "thumbee", + "neon", + "vfpv3", + "vfpv3d16", + "tls", + "vfpv4", + "idiva", + "idivt", + "vfpd32", + "lpae", + "evtstrm" +}; + +static const char *compat_hwcap2_str[] = { + "aes", + "pmull", + "sha1", + "sha2", + "crc32", + NULL +}; +#endif /* CONFIG_COMPAT */ + static int c_show(struct seq_file *m, void *v) { - int i; - - seq_printf(m, "Processor\t: %s rev %d (%s)\n", - cpu_name, read_cpuid_id() & 15, ELF_PLATFORM); + int i, j; for_each_online_cpu(i) { + struct cpuinfo_arm64 *cpuinfo = &per_cpu(cpu_data, i); + u32 midr = cpuinfo->reg_midr; + /* * glibc reads /proc/cpuinfo to determine the number of * online processors, looking for lines beginning with @@ -448,35 +485,38 @@ static int c_show(struct seq_file *m, void *v) #ifdef CONFIG_SMP seq_printf(m, "processor\t: %d\n", i); #endif - } - /* dump out the processor features */ - seq_puts(m, "Features\t: "); - - for (i = 0; hwcap_str[i]; i++) - if (elf_hwcap & (1 << i)) - seq_printf(m, "%s ", hwcap_str[i]); -#ifdef CONFIG_ARMV7_COMPAT_CPUINFO - if (is_compat_task()) { - /* Print out the non-optional ARMv8 HW capabilities */ - seq_printf(m, "wp half thumb fastmult vfp edsp neon vfpv3 tlsi "); - seq_printf(m, "vfpv4 idiva idivt "); + /* + * Dump out the common processor features in a single line. + * Userspace should read the hwcaps with getauxval(AT_HWCAP) + * rather than attempting to parse this, but there's a body of + * software which does already (at least for 32-bit). + */ + seq_puts(m, "Features\t:"); + if (personality(current->personality) == PER_LINUX32) { +#ifdef CONFIG_COMPAT + for (j = 0; compat_hwcap_str[j]; j++) + if (compat_elf_hwcap & (1 << j)) + seq_printf(m, " %s", compat_hwcap_str[j]); + + for (j = 0; compat_hwcap2_str[j]; j++) + if (compat_elf_hwcap2 & (1 << j)) + seq_printf(m, " %s", compat_hwcap2_str[j]); +#endif /* CONFIG_COMPAT */ + } else { + for (j = 0; hwcap_str[j]; j++) + if (elf_hwcap & (1 << j)) + seq_printf(m, " %s", hwcap_str[j]); + } + seq_puts(m, "\n"); + + seq_printf(m, "CPU implementer\t: 0x%02x\n", + MIDR_IMPLEMENTOR(midr)); + seq_printf(m, "CPU architecture: 8\n"); + seq_printf(m, "CPU variant\t: 0x%x\n", MIDR_VARIANT(midr)); + seq_printf(m, "CPU part\t: 0x%03x\n", MIDR_PARTNUM(midr)); + seq_printf(m, "CPU revision\t: %d\n\n", MIDR_REVISION(midr)); } -#endif - - seq_printf(m, "\nCPU implementer\t: 0x%02x\n", read_cpuid_id() >> 24); - seq_printf(m, "CPU architecture: %s\n", -#if IS_ENABLED(CONFIG_ARMV7_COMPAT_CPUINFO) - is_compat_task() ? "8" : -#endif - "AArch64"); - seq_printf(m, "CPU variant\t: 0x%x\n", (read_cpuid_id() >> 20) & 15); - seq_printf(m, "CPU part\t: 0x%03x\n", (read_cpuid_id() >> 4) & 0xfff); - seq_printf(m, "CPU revision\t: %d\n", read_cpuid_id() & 15); - - seq_puts(m, "\n"); - - seq_printf(m, "Hardware\t: %s\n", machine_name); return 0; } diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c index 9b43b410d039..0928460caada 100644 --- a/arch/arm64/kernel/signal32.c +++ b/arch/arm64/kernel/signal32.c @@ -445,7 +445,7 @@ static void compat_setup_return(struct pt_regs *regs, struct k_sigaction *ka, { compat_ulong_t handler = ptr_to_compat(ka->sa.sa_handler); compat_ulong_t retcode; - compat_ulong_t spsr = regs->pstate & ~PSR_f; + compat_ulong_t spsr = regs->pstate & ~(PSR_f | COMPAT_PSR_E_BIT); int thumb; /* Check if the handler is written for ARM or Thumb */ @@ -459,6 +459,9 @@ static void compat_setup_return(struct pt_regs *regs, struct k_sigaction *ka, /* The IT state must be cleared for both ARM and Thumb-2 */ spsr &= ~COMPAT_PSR_IT_MASK; + /* Restore the original endianness */ + spsr |= COMPAT_PSR_ENDSTATE; + if (ka->sa.sa_flags & SA_RESTORER) { retcode = ptr_to_compat(ka->sa.sa_restorer); } else { diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 6819fab9a80b..cba84d9a4c0b 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -39,6 +39,7 @@ #include <asm/atomic.h> #include <asm/cacheflush.h> +#include <asm/cpu.h> #include <asm/cputype.h> #include <asm/cpu_ops.h> #include <asm/mmu_context.h> @@ -155,6 +156,11 @@ asmlinkage void secondary_start_kernel(void) cpu_ops[cpu]->cpu_postboot(); /* + * Log the CPU info before it is marked online and might get read. + */ + cpuinfo_store_cpu(); + + /* * Enable GIC and timers. */ notify_cpu_starting(cpu); diff --git a/arch/arm64/kernel/swp_emulate.c b/arch/arm64/kernel/swp_emulate.c deleted file mode 100644 index 508fd2edb8ab..000000000000 --- a/arch/arm64/kernel/swp_emulate.c +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Derived from from linux/arch/arm/kernel/swp_emulate.c - * - * Copyright (C) 2009 ARM Limited - * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. - * - * 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. - * - * Implements emulation of the SWP/SWPB instructions using load-exclusive and - * store-exclusive for processors that have them disabled (or future ones that - * might not implement them). - * - * Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>] - * Where: Rt = destination - * Rt2 = source - * Rn = address - */ - -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/proc_fs.h> -#include <linux/seq_file.h> -#include <linux/sched.h> -#include <linux/syscalls.h> -#include <linux/perf_event.h> - -#include <asm/opcodes.h> -#include <asm/traps.h> -#include <asm/uaccess.h> -#include <asm/system_misc.h> -#include <linux/debugfs.h> - -/* - * Error-checking SWP macros implemented using ldrex{b}/strex{b} - */ - -static int swpb(u8 in, u8 *out, u8 *addr) -{ - u8 _out; - int res; - int err; - - do { - __asm__ __volatile__( - "0: ldxrb %w1, %4\n" - "1: stxrb %w0, %w3, %4\n" - " mov %w2, #0\n" - "2:\n" - " .section .fixup,\"ax\"\n" - " .align 2\n" - "3: mov %w2, %5\n" - " b 2b\n" - " .previous\n" - " .section __ex_table,\"a\"\n" - " .align 3\n" - " .quad 0b, 3b\n" - " .quad 1b, 3b\n" - " .previous" - : "=&r" (res), "=r" (_out), "=r" (err) - : "r" (in), "Q" (*addr), "i" (-EFAULT) - : "cc", "memory"); - } while (err == 0 && res != 0); - - if (err == 0) - *out = _out; - return err; -} - -static int swp(u32 in, u32 *out, u32 *addr) -{ - u32 _out; - int res; - int err = 0; - - do { - __asm__ __volatile__( - "0: ldxr %w1, %4\n" - "1: stxr %w0, %w3, %4\n" - " mov %w2, #0\n" - "2:\n" - " .section .fixup,\"ax\"\n" - " .align 2\n" - "3: mov %w2, %5\n" - " b 2b\n" - " .previous\n" - " .section __ex_table,\"a\"\n" - " .align 3\n" - " .quad 0b, 3b\n" - " .quad 1b, 3b\n" - " .previous" - : "=&r" (res), "=r" (_out), "=r" (err) - : "r" (in), "Q" (*addr), "i" (-EFAULT) - : "cc", "memory"); - } while (err == 0 && res != 0); - - if (err == 0) - *out = _out; - return err; -} -/* - * Macros/defines for extracting register numbers from instruction. - */ -#define EXTRACT_REG_NUM(instruction, offset) \ - (((instruction) & (0xf << (offset))) >> (offset)) -#define RN_OFFSET 16 -#define RT_OFFSET 12 -#define RT2_OFFSET 0 -/* - * Bit 22 of the instruction encoding distinguishes between - * the SWP and SWPB variants (bit set means SWPB). - */ -#define TYPE_SWPB (1 << 22) - -static pid_t previous_pid; - -u64 swpb_count = 0; -u64 swp_count = 0; - -/* - * swp_handler logs the id of calling process, dissects the instruction, sanity - * checks the memory location, calls emulate_swpX for the actual operation and - * deals with fixup/error handling before returning - */ -static int swp_handler(struct pt_regs *regs, unsigned int instr) -{ - u32 destreg, data, type; - uintptr_t address; - unsigned int res = 0; - int err; - u32 temp32; - u8 temp8; - - perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc); - - res = arm_check_condition(instr, regs->pstate); - switch (res) { - case ARM_OPCODE_CONDTEST_PASS: - break; - case ARM_OPCODE_CONDTEST_FAIL: - /* Condition failed - return to next instruction */ - regs->pc += 4; - return 0; - case ARM_OPCODE_CONDTEST_UNCOND: - /* If unconditional encoding - not a SWP, undef */ - return -EFAULT; - default: - return -EINVAL; - } - - if (current->pid != previous_pid) { - pr_warn("\"%s\" (%ld) uses obsolete SWP{B} instruction\n", - current->comm, (unsigned long)current->pid); - previous_pid = current->pid; - } - - address = regs->regs[EXTRACT_REG_NUM(instr, RN_OFFSET)] & 0xffffffff; - data = regs->regs[EXTRACT_REG_NUM(instr, RT2_OFFSET)]; - destreg = EXTRACT_REG_NUM(instr, RT_OFFSET); - - type = instr & TYPE_SWPB; - - /* Check access in reasonable access range for both SWP and SWPB */ - if (!access_ok(VERIFY_WRITE, (address & ~3), 4)) { - pr_debug("SWP{B} emulation: access to %p not allowed!\n", - (void *)address); - res = -EFAULT; - } - if (type == TYPE_SWPB) { - err = swpb((u8) data, &temp8, (u8 *) address); - if (err) - return err; - regs->regs[destreg] = temp8; - regs->pc += 4; - swpb_count++; - } else if (address & 0x3) { - /* SWP to unaligned address not permitted */ - pr_debug("SWP instruction on unaligned pointer!\n"); - return -EFAULT; - } else { - err = swp((u32) data, &temp32, (u32 *) address); - if (err) - return err; - regs->regs[destreg] = temp32; - regs->pc += 4; - swp_count++; - } - - return 0; -} - -/* - * Only emulate SWP/SWPB executed in ARM state/User mode. - * The kernel must be SWP free and SWP{B} does not exist in Thumb/ThumbEE. - */ -static struct undef_hook swp_hook = { - .instr_mask = 0x0fb00ff0, - .instr_val = 0x01000090, - .pstate_mask = COMPAT_PSR_MODE_MASK | COMPAT_PSR_T_BIT, - .pstate_val = COMPAT_PSR_MODE_USR, - .fn = swp_handler -}; - -/* - * Register handler and create status file in /proc/cpu - * Invoked as late_initcall, since not needed before init spawned. - */ -static int __init swp_emulation_init(void) -{ - struct dentry *dir; - dir = debugfs_create_dir("swp_emulate", NULL); - debugfs_create_u64("swp_count", S_IRUGO | S_IWUSR, dir, &swp_count); - debugfs_create_u64("swpb_count", S_IRUGO | S_IWUSR, dir, &swpb_count); - - pr_notice("Registering SWP/SWPB emulation handler\n"); - register_undef_hook(&swp_hook); - - - return 0; -} - -late_initcall(swp_emulation_init); diff --git a/arch/arm64/kernel/trace-events-emulation.h b/arch/arm64/kernel/trace-events-emulation.h new file mode 100644 index 000000000000..ae1dd598ea65 --- /dev/null +++ b/arch/arm64/kernel/trace-events-emulation.h @@ -0,0 +1,35 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM emulation + +#if !defined(_TRACE_EMULATION_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_EMULATION_H + +#include <linux/tracepoint.h> + +TRACE_EVENT(instruction_emulation, + + TP_PROTO(const char *instr, u64 addr), + TP_ARGS(instr, addr), + + TP_STRUCT__entry( + __string(instr, instr) + __field(u64, addr) + ), + + TP_fast_assign( + __assign_str(instr, instr); + __entry->addr = addr; + ), + + TP_printk("instr=\"%s\" addr=0x%llx", __get_str(instr), __entry->addr) +); + +#endif /* _TRACE_EMULATION_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_PATH . + +#define TRACE_INCLUDE_FILE trace-events-emulation +#include <trace/define_trace.h> diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index 0da47699510b..943a30e9b839 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -3,7 +3,6 @@ * * Copyright (C) 1995-2009 Russell King * Copyright (C) 2012 ARM Ltd. - * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. * * 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 @@ -34,6 +33,7 @@ #include <asm/atomic.h> #include <asm/debug-monitors.h> +#include <asm/insn.h> #include <asm/traps.h> #include <asm/stacktrace.h> #include <asm/exception.h> @@ -259,57 +259,80 @@ void arm64_notify_die(const char *str, struct pt_regs *regs, } static LIST_HEAD(undef_hook); +static DEFINE_RAW_SPINLOCK(undef_lock); void register_undef_hook(struct undef_hook *hook) { + unsigned long flags; + + raw_spin_lock_irqsave(&undef_lock, flags); list_add(&hook->node, &undef_hook); + raw_spin_unlock_irqrestore(&undef_lock, flags); +} + +void unregister_undef_hook(struct undef_hook *hook) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&undef_lock, flags); + list_del(&hook->node); + raw_spin_unlock_irqrestore(&undef_lock, flags); } -static int call_undef_hook(struct pt_regs *regs, unsigned int instr) +static int call_undef_hook(struct pt_regs *regs) { struct undef_hook *hook; - int (*fn)(struct pt_regs *regs, unsigned int instr) = NULL; + unsigned long flags; + u32 instr; + int (*fn)(struct pt_regs *regs, u32 instr) = NULL; + void __user *pc = (void __user *)instruction_pointer(regs); + + if (!user_mode(regs)) + return 1; + + if (compat_thumb_mode(regs)) { + /* 16-bit Thumb instruction */ + if (get_user(instr, (u16 __user *)pc)) + goto exit; + instr = le16_to_cpu(instr); + if (aarch32_insn_is_wide(instr)) { + u32 instr2; + + if (get_user(instr2, (u16 __user *)(pc + 2))) + goto exit; + instr2 = le16_to_cpu(instr2); + instr = (instr << 16) | instr2; + } + } else { + /* 32-bit ARM instruction */ + if (get_user(instr, (u32 __user *)pc)) + goto exit; + instr = le32_to_cpu(instr); + } + raw_spin_lock_irqsave(&undef_lock, flags); list_for_each_entry(hook, &undef_hook, node) if ((instr & hook->instr_mask) == hook->instr_val && - (regs->pstate & hook->pstate_mask) == hook->pstate_val) + (regs->pstate & hook->pstate_mask) == hook->pstate_val) fn = hook->fn; + raw_spin_unlock_irqrestore(&undef_lock, flags); +exit: return fn ? fn(regs, instr) : 1; } asmlinkage void __exception do_undefinstr(struct pt_regs *regs) { - u32 instr; siginfo_t info; void __user *pc = (void __user *)instruction_pointer(regs); /* check for AArch32 breakpoint instructions */ if (!aarch32_break_handler(regs)) return; - if (user_mode(regs)) { - if (compat_thumb_mode(regs)) { - if (get_user(instr, (u16 __user *)pc)) - goto die_sig; - if (is_wide_instruction(instr)) { - u32 instr2; - if (get_user(instr2, (u16 __user *)pc+1)) - goto die_sig; - instr <<= 16; - instr |= instr2; - } - } else if (get_user(instr, (u32 __user *)pc)) { - goto die_sig; - } - } else { - /* kernel mode */ - instr = *((u32 *)pc); - } - if (call_undef_hook(regs, instr) == 0) + if (call_undef_hook(regs) == 0) return; -die_sig: if (show_unhandled_signals && unhandled_signal(current, SIGILL) && printk_ratelimit()) { pr_info("%s[%d]: undefined instruction: pc=%p\n", diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S index e83249401615..e085ee6ef4e2 100644 --- a/arch/arm64/mm/proc.S +++ b/arch/arm64/mm/proc.S @@ -225,20 +225,6 @@ ENTRY(__cpu_setup) ret // return to head.S ENDPROC(__cpu_setup) -#ifdef CONFIG_ARMV7_COMPAT - /* - * n n T - * U E WT T UD US IHBS - * CE0 XWHW CZ ME TEEA S - * .... .IEE .... NEAI TE.I ..AD DEN0 ACAM - * 0011 0... 1101 ..0. ..0. 10.. .... .... < hardware reserved - * .... .100 .... 01.1 11.1 ..01 0011 1101 < software settings - */ - .type crval, #object -crval: - .word 0x030802e2 // clear - .word 0x0405d03d // set -#else /* * n n T * U E WT T UD US IHBS @@ -251,4 +237,3 @@ crval: crval: .word 0x000802e2 // clear .word 0x0405d11d // set -#endif diff --git a/drivers/staging/android/uapi/ashmem.h b/drivers/staging/android/uapi/ashmem.h index ba4743c71d6b..13df42d200b7 100644 --- a/drivers/staging/android/uapi/ashmem.h +++ b/drivers/staging/android/uapi/ashmem.h @@ -13,6 +13,7 @@ #define _UAPI_LINUX_ASHMEM_H #include <linux/ioctl.h> +#include <linux/types.h> #define ASHMEM_NAME_LEN 256 diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 558995df161b..dfb1c4c4b0c7 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -550,6 +550,12 @@ config USB_F_MASS_STORAGE config USB_F_FS tristate +config USB_F_MTP + tristate + +config USB_F_PTP + tristate + choice tristate "USB Gadget Drivers" default USB_ETH @@ -722,6 +728,28 @@ config USB_CONFIGFS_F_FS implemented in kernel space (for instance Ethernet, serial or mass storage) and other are implemented in user space. +config USB_CONFIGFS_F_MTP + boolean "MTP gadget" + depends on USB_CONFIGFS + select USB_F_MTP + help + USB gadget MTP support + +config USB_CONFIGFS_F_PTP + boolean "PTP gadget" + depends on USB_CONFIGFS && USB_CONFIGFS_F_MTP + select USB_F_PTP + help + USB gadget PTP support + +config USB_CONFIGFS_UEVENT + boolean "Uevent notification of Gadget state" + depends on USB_CONFIGFS + help + Enable uevent notifications to userspace when the gadget + state changes. The gadget can be in any of the following + three states: "CONNECTED/DISCONNECTED/CONFIGURED" + config USB_ZERO tristate "Gadget Zero (DEVELOPMENT)" select USB_LIBCOMPOSITE diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 98af79e679e8..b2b05d5ed08a 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -64,6 +64,10 @@ usb_f_mass_storage-y := f_mass_storage.o storage_common.o obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o usb_f_fs-y := f_fs.o obj-$(CONFIG_USB_F_FS) += usb_f_fs.o +usb_f_mtp-y := f_mtp.o +obj-$(CONFIG_USB_F_MTP) += usb_f_mtp.o +usb_f_ptp-y := f_ptp.o +obj-$(CONFIG_USB_F_PTP) += usb_f_ptp.o # # USB gadget drivers diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index 91f0efdabc8a..a1398df9e482 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -91,6 +91,9 @@ struct android_dev { struct usb_composite_dev *cdev; struct device *dev; + void (*setup_complete)(struct usb_ep *ep, + struct usb_request *req); + bool enabled; int disable_depth; struct mutex mutex; @@ -1354,6 +1357,9 @@ static int android_bind(struct usb_composite_dev *cdev) struct usb_gadget *gadget = cdev->gadget; int id, ret; + /* Save the default handler */ + dev->setup_complete = cdev->req->complete; + /* * Start disconnected. Userspace will connect the gadget once * it is done configuring the functions. @@ -1420,6 +1426,7 @@ android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c) req->zero = 0; req->length = 0; + req->complete = dev->setup_complete; gadget->ep0->driver_data = cdev; list_for_each_entry(f, &dev->enabled_functions, enabled_list) { diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 7d1cc01796b6..c0e3a7da3430 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -6,6 +6,20 @@ #include <linux/usb/gadget_configfs.h> #include "configfs.h" +#ifdef CONFIG_USB_CONFIGFS_UEVENT +#include <linux/platform_device.h> +#include <linux/kdev_t.h> +#include <linux/usb/ch9.h> +#include "u_fs.h" + +#ifdef CONFIG_USB_CONFIGFS_F_ACC +extern int acc_ctrlrequest(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl); +void acc_disconnect(void); +#endif +static struct class *android_class; +#endif + int check_user_usb_string(const char *name, struct usb_gadget_strings *stringtab_dev) { @@ -56,6 +70,12 @@ struct gadget_info { #endif struct usb_composite_driver composite; struct usb_composite_dev cdev; +#ifdef CONFIG_USB_CONFIGFS_UEVENT + bool connected; + bool sw_connected; + struct work_struct work; + struct device *dev; +#endif }; struct config_usb_cfg { @@ -251,7 +271,7 @@ static ssize_t gadget_dev_desc_UDC_store(struct gadget_info *gi, mutex_lock(&gi->lock); - if (!strlen(name)) { + if (!strlen(name) || strcmp(name, "none") == 0) { ret = unregister_gadget(gi); if (ret) goto err; @@ -884,6 +904,57 @@ err_comp_cleanup: return ret; } +#ifdef CONFIG_USB_CONFIGFS_UEVENT +static void android_work(struct work_struct *data) +{ + struct gadget_info *gi = container_of(data, struct gadget_info, work); + struct usb_composite_dev *cdev = &gi->cdev; + char *disconnected[2] = { "USB_STATE=DISCONNECTED", NULL }; + char *connected[2] = { "USB_STATE=CONNECTED", NULL }; + char *configured[2] = { "USB_STATE=CONFIGURED", NULL }; + /* 0-connected 1-configured 2-disconnected*/ + bool status[3] = { false, false, false }; + unsigned long flags; + bool uevent_sent = false; + + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + status[1] = true; + + if (gi->connected != gi->sw_connected) { + if (gi->connected) + status[0] = true; + else + status[2] = true; + gi->sw_connected = gi->connected; + } + spin_unlock_irqrestore(&cdev->lock, flags); + + if (status[0]) { + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, connected); + pr_info("%s: sent uevent %s\n", __func__, connected[0]); + uevent_sent = true; + } + + if (status[1]) { + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, configured); + pr_info("%s: sent uevent %s\n", __func__, configured[0]); + uevent_sent = true; + } + + if (status[2]) { + kobject_uevent_env(&gi->dev->kobj, KOBJ_CHANGE, disconnected); + pr_info("%s: sent uevent %s\n", __func__, disconnected[0]); + uevent_sent = true; + } + + if (!uevent_sent) { + pr_info("%s: did not send uevent (%d %d %p)\n", __func__, + gi->connected, gi->sw_connected, cdev->config); + } +} +#endif + static void configfs_composite_unbind(struct usb_gadget *gadget) { struct usb_composite_dev *cdev; @@ -901,13 +972,77 @@ static void configfs_composite_unbind(struct usb_gadget *gadget) set_gadget_data(gadget, NULL); } +#ifdef CONFIG_USB_CONFIGFS_UEVENT +static int android_setup(struct usb_gadget *gadget, + const struct usb_ctrlrequest *c) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + unsigned long flags; + struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); + int value = -EOPNOTSUPP; + struct usb_function_instance *fi; + + spin_lock_irqsave(&cdev->lock, flags); + if (!gi->connected) { + gi->connected = 1; + schedule_work(&gi->work); + } + spin_unlock_irqrestore(&cdev->lock, flags); + list_for_each_entry(fi, &gi->available_func, cfs_list) { + if (fi != NULL && fi->f != NULL && fi->f->setup != NULL) { + value = fi->f->setup(fi->f, c); + if (value >= 0) + break; + } + } + +#ifdef CONFIG_USB_CONFIGFS_F_ACC + if (value < 0) + value = acc_ctrlrequest(cdev, c); +#endif + + if (value < 0) + value = composite_setup(gadget, c); + + spin_lock_irqsave(&cdev->lock, flags); + if (c->bRequest == USB_REQ_SET_CONFIGURATION && + cdev->config) { + schedule_work(&gi->work); + } + spin_unlock_irqrestore(&cdev->lock, flags); + + return value; +} + +static void android_disconnect(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev); + + /* accessory HID support can be active while the + accessory function is not actually enabled, + so we need to inform it when we are disconnected. + */ + +#ifdef CONFIG_USB_CONFIGFS_F_ACC + acc_disconnect(); +#endif + gi->connected = 0; + schedule_work(&gi->work); + composite_disconnect(gadget); +} +#endif + static const struct usb_gadget_driver configfs_driver_template = { .bind = configfs_composite_bind, .unbind = configfs_composite_unbind, - +#ifdef CONFIG_USB_CONFIGFS_UEVENT + .setup = android_setup, + .disconnect = android_disconnect, +#else .setup = composite_setup, .disconnect = composite_disconnect, - +#endif .max_speed = USB_SPEED_SUPER, .driver = { .owner = THIS_MODULE, @@ -957,6 +1092,12 @@ static struct config_group *gadgets_make( gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL); gi->composite.name = gi->composite.gadget_driver.function; +#ifdef CONFIG_USB_CONFIGFS_UEVENT + INIT_WORK(&gi->work, android_work); + gi->dev = device_create(android_class, NULL, + MKDEV(0, 0), NULL, "android0"); +#endif + if (!gi->composite.gadget_driver.function) goto err; @@ -1014,6 +1155,13 @@ static int __init gadget_cfs_init(void) config_group_init(&gadget_subsys.su_group); ret = configfs_register_subsystem(&gadget_subsys); + +#ifdef CONFIG_USB_CONFIGFS_UEVENT + android_class = class_create(THIS_MODULE, "android_usb"); + if (IS_ERR(android_class)) + return PTR_ERR(android_class); +#endif + return ret; } module_init(gadget_cfs_init); diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c index c0397f1116ac..dcedeb4b2fef 100644 --- a/drivers/usb/gadget/f_accessory.c +++ b/drivers/usb/gadget/f_accessory.c @@ -951,6 +951,10 @@ kill_all_hid_devices(struct acc_dev *dev) struct list_head *entry, *temp; unsigned long flags; + /* do nothing if usb accessory device doesn't exist */ + if (!dev) + return; + spin_lock_irqsave(&dev->lock, flags); list_for_each_safe(entry, temp, &dev->hid_list) { hid = list_entry(entry, struct acc_hid_dev, list); diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c index 620aeaaf2d72..82f6b2ebaebb 100644 --- a/drivers/usb/gadget/f_mtp.c +++ b/drivers/usb/gadget/f_mtp.c @@ -35,9 +35,14 @@ #include <linux/usb_usual.h> #include <linux/usb/ch9.h> #include <linux/usb/f_mtp.h> +#include <linux/configfs.h> +#include <linux/usb/composite.h> + +#include "configfs.h" #define MTP_BULK_BUFFER_SIZE 16384 #define INTR_BUFFER_SIZE 28 +#define MAX_INST_NAME_LEN 40 /* String IDs */ #define INTERFACE_STRING_INDEX 0 @@ -66,8 +71,9 @@ /* constants for device status */ #define MTP_RESPONSE_OK 0x2001 #define MTP_RESPONSE_DEVICE_BUSY 0x2019 +#define DRIVER_NAME "mtp" -static const char mtp_shortname[] = "mtp_usb"; +static const char mtp_shortname[] = DRIVER_NAME "_usb"; struct mtp_dev { struct usb_function function; @@ -280,6 +286,12 @@ struct mtp_data_header { __le32 transaction_id; }; +struct mtp_instance { + struct usb_function_instance func_inst; + const char *name; + struct mtp_dev *dev; +}; + /* temporary variable used between mtp_open() and mtp_gadget_bind() */ static struct mtp_dev *_mtp_dev; @@ -456,7 +468,7 @@ static int mtp_create_bulk_endpoints(struct mtp_dev *dev, return 0; fail: - printk(KERN_ERR "mtp_bind() could not allocate requests\n"); + pr_err("mtp_bind() could not allocate requests\n"); return -1; } @@ -1099,6 +1111,13 @@ mtp_function_bind(struct usb_configuration *c, struct usb_function *f) return id; mtp_interface_desc.bInterfaceNumber = id; + if (mtp_string_defs[INTERFACE_STRING_INDEX].id == 0) { + ret = usb_string_id(c->cdev); + if (ret < 0) + return ret; + mtp_string_defs[INTERFACE_STRING_INDEX].id = ret; + mtp_interface_desc.iInterface = ret; + } /* allocate endpoints */ ret = mtp_create_bulk_endpoints(dev, &mtp_fullspeed_in_desc, &mtp_fullspeed_out_desc, &mtp_intr_desc); @@ -1126,6 +1145,7 @@ mtp_function_unbind(struct usb_configuration *c, struct usb_function *f) struct usb_request *req; int i; + mtp_string_defs[INTERFACE_STRING_INDEX].id = 0; while ((req = mtp_req_get(dev, &dev->tx_idle))) mtp_request_free(req, dev->ep_in); for (i = 0; i < RX_REQ_MAX; i++) @@ -1213,7 +1233,7 @@ static int mtp_bind_config(struct usb_configuration *c, bool ptp_config) } dev->cdev = c->cdev; - dev->function.name = "mtp"; + dev->function.name = DRIVER_NAME; dev->function.strings = mtp_strings; if (ptp_config) { dev->function.fs_descriptors = fs_ptp_descs; @@ -1230,12 +1250,16 @@ static int mtp_bind_config(struct usb_configuration *c, bool ptp_config) return usb_add_function(c, &dev->function); } -static int mtp_setup(void) +static int __mtp_setup(struct mtp_instance *fi_mtp) { struct mtp_dev *dev; int ret; dev = kzalloc(sizeof(*dev), GFP_KERNEL); + + if (fi_mtp != NULL) + fi_mtp->dev = dev; + if (!dev) return -ENOMEM; @@ -1273,6 +1297,17 @@ err1: return ret; } +static int mtp_setup(void) +{ + return __mtp_setup(NULL); +} + +static int mtp_setup_configfs(struct mtp_instance *fi_mtp) +{ + return __mtp_setup(fi_mtp); +} + + static void mtp_cleanup(void) { struct mtp_dev *dev = _mtp_dev; @@ -1285,3 +1320,138 @@ static void mtp_cleanup(void) _mtp_dev = NULL; kfree(dev); } + +static struct mtp_instance *to_mtp_instance(struct config_item *item) +{ + return container_of(to_config_group(item), struct mtp_instance, + func_inst.group); +} + +static void mtp_attr_release(struct config_item *item) +{ + struct mtp_instance *fi_mtp = to_mtp_instance(item); + usb_put_function_instance(&fi_mtp->func_inst); +} + +static struct configfs_item_operations mtp_item_ops = { + .release = mtp_attr_release, +}; + +static struct config_item_type mtp_func_type = { + .ct_item_ops = &mtp_item_ops, + .ct_owner = THIS_MODULE, +}; + + +static struct mtp_instance *to_fi_mtp(struct usb_function_instance *fi) +{ + return container_of(fi, struct mtp_instance, func_inst); +} + +static int mtp_set_inst_name(struct usb_function_instance *fi, const char *name) +{ + struct mtp_instance *fi_mtp; + char *ptr; + int name_len; + + name_len = strlen(name) + 1; + if (name_len > MAX_INST_NAME_LEN) + return -ENAMETOOLONG; + + ptr = kstrndup(name, name_len, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + fi_mtp = to_fi_mtp(fi); + fi_mtp->name = ptr; + + return 0; +} + +static void mtp_free_inst(struct usb_function_instance *fi) +{ + struct mtp_instance *fi_mtp; + + fi_mtp = to_fi_mtp(fi); + kfree(fi_mtp->name); + mtp_cleanup(); + kfree(fi_mtp); +} + +struct usb_function_instance *alloc_inst_mtp_ptp(bool mtp_config) +{ + struct mtp_instance *fi_mtp; + int ret = 0; + + fi_mtp = kzalloc(sizeof(*fi_mtp), GFP_KERNEL); + if (!fi_mtp) + return ERR_PTR(-ENOMEM); + fi_mtp->func_inst.set_inst_name = mtp_set_inst_name; + fi_mtp->func_inst.free_func_inst = mtp_free_inst; + + if (mtp_config) { + ret = mtp_setup_configfs(fi_mtp); + if (ret) { + kfree(fi_mtp); + pr_err("Error setting MTP\n"); + return ERR_PTR(ret); + } + } else + fi_mtp->dev = _mtp_dev; + + config_group_init_type_name(&fi_mtp->func_inst.group, + "", &mtp_func_type); + + return &fi_mtp->func_inst; +} +EXPORT_SYMBOL_GPL(alloc_inst_mtp_ptp); + +static struct usb_function_instance *mtp_alloc_inst(void) +{ + return alloc_inst_mtp_ptp(true); +} + +static int mtp_ctrlreq_configfs(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + return mtp_ctrlrequest(f->config->cdev, ctrl); +} + +static void mtp_free(struct usb_function *f) +{ + /*NO-OP: no function specific resource allocation in mtp_alloc*/ +} + +struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi, + bool mtp_config) +{ + struct mtp_instance *fi_mtp = to_fi_mtp(fi); + struct mtp_dev *dev = fi_mtp->dev; + + dev->function.name = DRIVER_NAME; + dev->function.strings = mtp_strings; + if (mtp_config) { + dev->function.fs_descriptors = fs_mtp_descs; + dev->function.hs_descriptors = hs_mtp_descs; + } else { + dev->function.fs_descriptors = fs_ptp_descs; + dev->function.hs_descriptors = hs_ptp_descs; + } + dev->function.bind = mtp_function_bind; + dev->function.unbind = mtp_function_unbind; + dev->function.set_alt = mtp_function_set_alt; + dev->function.disable = mtp_function_disable; + dev->function.setup = mtp_ctrlreq_configfs; + dev->function.free_func = mtp_free; + + return &dev->function; +} +EXPORT_SYMBOL_GPL(function_alloc_mtp_ptp); + +static struct usb_function *mtp_alloc(struct usb_function_instance *fi) +{ + return function_alloc_mtp_ptp(fi, true); +} + +DECLARE_USB_FUNCTION_INIT(mtp, mtp_alloc_inst, mtp_alloc); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/f_mtp.h b/drivers/usb/gadget/f_mtp.h new file mode 100644 index 000000000000..7adb1ff08eff --- /dev/null +++ b/drivers/usb/gadget/f_mtp.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2014 Google, Inc. + * Author: Badhri Jagan Sridharan <badhri@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +extern struct usb_function_instance *alloc_inst_mtp_ptp(bool mtp_config); +extern struct usb_function *function_alloc_mtp_ptp( + struct usb_function_instance *fi, bool mtp_config); diff --git a/drivers/usb/gadget/f_ptp.c b/drivers/usb/gadget/f_ptp.c new file mode 100644 index 000000000000..da3e4d53e085 --- /dev/null +++ b/drivers/usb/gadget/f_ptp.c @@ -0,0 +1,38 @@ +/* + * Gadget Function Driver for PTP + * + * Copyright (C) 2014 Google, Inc. + * Author: Badhri Jagan Sridharan <badhri@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/types.h> + +#include <linux/configfs.h> +#include <linux/usb/composite.h> + +#include "f_mtp.h" + +static struct usb_function_instance *ptp_alloc_inst(void) +{ + return alloc_inst_mtp_ptp(false); +} + +static struct usb_function *ptp_alloc(struct usb_function_instance *fi) +{ + return function_alloc_mtp_ptp(fi, false); +} + +DECLARE_USB_FUNCTION_INIT(ptp, ptp_alloc_inst, ptp_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Badhri Jagan Sridharan"); diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index dba63f53906c..dcae90e071ec 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -468,6 +468,7 @@ struct usb_function_instance { struct config_group group; struct list_head cfs_list; struct usb_function_driver *fd; + struct usb_function *f; int (*set_inst_name)(struct usb_function_instance *inst, const char *name); void (*free_func_inst)(struct usb_function_instance *inst); diff --git a/include/uapi/linux/netfilter/xt_socket.h b/include/uapi/linux/netfilter/xt_socket.h index c9b247c0bfa6..55076a32bdfd 100644 --- a/include/uapi/linux/netfilter/xt_socket.h +++ b/include/uapi/linux/netfilter/xt_socket.h @@ -18,7 +18,6 @@ struct xt_socket_mtinfo2 { }; #define XT_SOCKET_FLAGS_V2 (XT_SOCKET_TRANSPARENT | XT_SOCKET_NOWILDCARD) -void xt_socket_put_sk(struct sock *sk); struct sock *xt_socket_get4_sk(const struct sk_buff *skb, struct xt_action_param *par); struct sock *xt_socket_get6_sk(const struct sk_buff *skb, diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 084967225f2e..87603f69eabc 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1659,6 +1659,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) struct sock *sk; kuid_t sock_uid; bool res; + bool set_sk_callback_lock = false; if (unlikely(module_passive)) return (info->match ^ info->invert) == 0; @@ -1716,6 +1717,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n", par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par)); if (sk != NULL) { + set_sk_callback_lock = true; + read_lock_bh(&sk->sk_callback_lock); MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", par->hooknum, sk, sk->sk_socket, sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); @@ -1802,6 +1805,8 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) put_sock_ret_res: if (got_sock) sock_gen_put(sk); + if (set_sk_callback_lock) + read_unlock_bh(&sk->sk_callback_lock); ret_res: MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res); return res; diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index c2d565bdb484..7eb9d7d534d6 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -35,16 +35,6 @@ #include <net/netfilter/nf_conntrack.h> #endif -void -xt_socket_put_sk(struct sock *sk) -{ - if (sk->sk_state == TCP_TIME_WAIT) - inet_twsk_put(inet_twsk(sk)); - else - sock_put(sk); -} -EXPORT_SYMBOL(xt_socket_put_sk); - static int extract_icmp4_fields(const struct sk_buff *skb, u8 *protocol, diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst index ecbb44797e28..8900c5c5390d 100644 --- a/scripts/Makefile.modinst +++ b/scripts/Makefile.modinst @@ -24,7 +24,7 @@ quiet_cmd_modules_install = INSTALL $@ INSTALL_MOD_DIR ?= extra ext-mod-dir = $(INSTALL_MOD_DIR)$(subst $(patsubst %/,%,$(KBUILD_EXTMOD)),,$(@D)) -modinst_dir = $(if $(KBUILD_EXTMOD),$(ext-mod-dir),kernel/$(@D)) +modinst_dir ?= $(if $(KBUILD_EXTMOD),$(ext-mod-dir),kernel/$(@D)) $(modules): $(call cmd,modules_install,$(MODLIB)/$(modinst_dir)) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 7c9a452b2d92..8c35f7eb35c1 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -161,6 +161,17 @@ static int selinux_peerlbl_enabled(void) return (selinux_policycap_alwaysnetwork || netlbl_enabled() || selinux_xfrm_enabled()); } +static int selinux_netcache_avc_callback(u32 event) +{ + if (event == AVC_CALLBACK_RESET) { + sel_netif_flush(); + sel_netnode_flush(); + sel_netport_flush(); + synchronize_net(); + } + return 0; +} + /* * initialise the security for the init task */ @@ -1953,12 +1964,10 @@ static int selinux_binder_transfer_file(struct task_struct *from, struct task_st struct inode *inode = file->f_path.dentry->d_inode; struct inode_security_struct *isec = inode->i_security; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; int rc; ad.type = LSM_AUDIT_DATA_PATH; ad.u.path = file->f_path; - ad.selinux_audit_data = &sad; if (sid != fsec->sid) { rc = avc_has_perm(sid, fsec->sid, @@ -3276,24 +3285,20 @@ error: static int selinux_mmap_addr(unsigned long addr) { - int rc = 0; - u32 sid = current_sid(); + int rc; + + /* do DAC check on address space usage */ + rc = cap_mmap_addr(addr); + if (rc) + return rc; - /* - * notice that we are intentionally putting the SELinux check before - * the secondary cap_file_mmap check. This is such a likely attempt - * at bad behaviour/exploit that we always want to get the AVC, even - * if DAC would have also denied the operation. - */ if (addr < CONFIG_LSM_MMAP_MIN_ADDR) { + u32 sid = current_sid(); rc = avc_has_perm(sid, sid, SECCLASS_MEMPROTECT, MEMPROTECT__MMAP_ZERO, NULL); - if (rc) - return rc; } - /* do DAC check on address space usage */ - return cap_mmap_addr(addr); + return rc; } static int selinux_mmap_file(struct file *file, unsigned long reqprot, @@ -4312,15 +4317,15 @@ static int selinux_socket_unix_may_send(struct socket *sock, &ad); } -static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family, - u32 peer_sid, +static int selinux_inet_sys_rcv_skb(struct net *ns, int ifindex, + char *addrp, u16 family, u32 peer_sid, struct common_audit_data *ad) { int err; u32 if_sid; u32 node_sid; - err = sel_netif_sid(ifindex, &if_sid); + err = sel_netif_sid(ns, ifindex, &if_sid); if (err) return err; err = avc_has_perm(peer_sid, if_sid, @@ -4413,8 +4418,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); if (err) return err; - err = selinux_inet_sys_rcv_skb(skb->skb_iif, addrp, family, - peer_sid, &ad); + err = selinux_inet_sys_rcv_skb(sock_net(sk), skb->skb_iif, + addrp, family, peer_sid, &ad); if (err) { selinux_netlbl_err(skb, err, 0); return err; @@ -4753,7 +4758,8 @@ out: #ifdef CONFIG_NETFILTER -static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, +static unsigned int selinux_ip_forward(struct sk_buff *skb, + const struct net_device *indev, u16 family) { int err; @@ -4779,14 +4785,14 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, ad.type = LSM_AUDIT_DATA_NET; ad.u.net = &net; - ad.u.net->netif = ifindex; + ad.u.net->netif = indev->ifindex; ad.u.net->family = family; if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0) return NF_DROP; if (peerlbl_active) { - err = selinux_inet_sys_rcv_skb(ifindex, addrp, family, - peer_sid, &ad); + err = selinux_inet_sys_rcv_skb(dev_net(indev), indev->ifindex, + addrp, family, peer_sid, &ad); if (err) { selinux_netlbl_err(skb, err, 1); return NF_DROP; @@ -4815,7 +4821,7 @@ static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return selinux_ip_forward(skb, in->ifindex, PF_INET); + return selinux_ip_forward(skb, in, PF_INET); } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) @@ -4825,7 +4831,7 @@ static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return selinux_ip_forward(skb, in->ifindex, PF_INET6); + return selinux_ip_forward(skb, in, PF_INET6); } #endif /* IPV6 */ @@ -4913,11 +4919,13 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, return NF_ACCEPT; } -static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, +static unsigned int selinux_ip_postroute(struct sk_buff *skb, + const struct net_device *outdev, u16 family) { u32 secmark_perm; u32 peer_sid; + int ifindex = outdev->ifindex; struct sock *sk; struct common_audit_data ad; struct lsm_network_audit net = {0,}; @@ -4998,6 +5006,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, case PF_INET6: if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) return NF_ACCEPT; + break; default: return NF_DROP_ERR(-ECONNREFUSED); } @@ -5029,7 +5038,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, u32 if_sid; u32 node_sid; - if (sel_netif_sid(ifindex, &if_sid)) + if (sel_netif_sid(dev_net(outdev), ifindex, &if_sid)) return NF_DROP; if (avc_has_perm(peer_sid, if_sid, SECCLASS_NETIF, NETIF__EGRESS, &ad)) @@ -5051,7 +5060,7 @@ static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return selinux_ip_postroute(skb, out->ifindex, PF_INET); + return selinux_ip_postroute(skb, out, PF_INET); } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) @@ -5061,7 +5070,7 @@ static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return selinux_ip_postroute(skb, out->ifindex, PF_INET6); + return selinux_ip_postroute(skb, out, PF_INET6); } #endif /* IPV6 */ @@ -6049,6 +6058,9 @@ static __init int selinux_init(void) if (register_security(&selinux_ops)) panic("SELinux: Unable to register with kernel.\n"); + if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET)) + panic("SELinux: Unable to register AVC netcache callback\n"); + if (selinux_enforcing) printk(KERN_DEBUG "SELinux: Starting in enforcing mode\n"); else diff --git a/security/selinux/include/netif.h b/security/selinux/include/netif.h index 43d507242b42..c72145444090 100644 --- a/security/selinux/include/netif.h +++ b/security/selinux/include/netif.h @@ -17,7 +17,11 @@ #ifndef _SELINUX_NETIF_H_ #define _SELINUX_NETIF_H_ -int sel_netif_sid(int ifindex, u32 *sid); +#include <net/net_namespace.h> + +void sel_netif_flush(void); + +int sel_netif_sid(struct net *ns, int ifindex, u32 *sid); #endif /* _SELINUX_NETIF_H_ */ diff --git a/security/selinux/include/netnode.h b/security/selinux/include/netnode.h index df7a5ed6c694..937668dd3024 100644 --- a/security/selinux/include/netnode.h +++ b/security/selinux/include/netnode.h @@ -27,6 +27,8 @@ #ifndef _SELINUX_NETNODE_H #define _SELINUX_NETNODE_H +void sel_netnode_flush(void); + int sel_netnode_sid(void *addr, u16 family, u32 *sid); #endif diff --git a/security/selinux/include/netport.h b/security/selinux/include/netport.h index 4d965b83d735..d1ce896b2cb0 100644 --- a/security/selinux/include/netport.h +++ b/security/selinux/include/netport.h @@ -26,6 +26,8 @@ #ifndef _SELINUX_NETPORT_H #define _SELINUX_NETPORT_H +void sel_netport_flush(void); + int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid); #endif diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 078e553f52f2..81fa718d5cb3 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -24,6 +24,7 @@ #include <linux/binfmts.h> #include <linux/in.h> #include <linux/spinlock.h> +#include <net/net_namespace.h> #include "flask.h" #include "avc.h" @@ -78,6 +79,7 @@ struct ipc_security_struct { }; struct netif_security_struct { + struct net *ns; /* network namespace */ int ifindex; /* device index */ u32 sid; /* SID for this interface */ }; diff --git a/security/selinux/netif.c b/security/selinux/netif.c index 694e9e43855f..485524c477a4 100644 --- a/security/selinux/netif.c +++ b/security/selinux/netif.c @@ -45,6 +45,7 @@ static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE]; /** * sel_netif_hashfn - Hashing function for the interface table + * @ns: the network namespace * @ifindex: the network interface * * Description: @@ -52,13 +53,14 @@ static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE]; * bucket number for the given interface. * */ -static inline u32 sel_netif_hashfn(int ifindex) +static inline u32 sel_netif_hashfn(const struct net *ns, int ifindex) { - return (ifindex & (SEL_NETIF_HASH_SIZE - 1)); + return (((uintptr_t)ns + ifindex) & (SEL_NETIF_HASH_SIZE - 1)); } /** * sel_netif_find - Search for an interface record + * @ns: the network namespace * @ifindex: the network interface * * Description: @@ -66,15 +68,15 @@ static inline u32 sel_netif_hashfn(int ifindex) * If an entry can not be found in the table return NULL. * */ -static inline struct sel_netif *sel_netif_find(int ifindex) +static inline struct sel_netif *sel_netif_find(const struct net *ns, + int ifindex) { - int idx = sel_netif_hashfn(ifindex); + int idx = sel_netif_hashfn(ns, ifindex); struct sel_netif *netif; list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list) - /* all of the devices should normally fit in the hash, so we - * optimize for that case */ - if (likely(netif->nsec.ifindex == ifindex)) + if (net_eq(netif->nsec.ns, ns) && + netif->nsec.ifindex == ifindex) return netif; return NULL; @@ -96,7 +98,7 @@ static int sel_netif_insert(struct sel_netif *netif) if (sel_netif_total >= SEL_NETIF_HASH_MAX) return -ENOSPC; - idx = sel_netif_hashfn(netif->nsec.ifindex); + idx = sel_netif_hashfn(netif->nsec.ns, netif->nsec.ifindex); list_add_rcu(&netif->list, &sel_netif_hash[idx]); sel_netif_total++; @@ -120,6 +122,7 @@ static void sel_netif_destroy(struct sel_netif *netif) /** * sel_netif_sid_slow - Lookup the SID of a network interface using the policy + * @ns: the network namespace * @ifindex: the network interface * @sid: interface SID * @@ -130,7 +133,7 @@ static void sel_netif_destroy(struct sel_netif *netif) * failure. * */ -static int sel_netif_sid_slow(int ifindex, u32 *sid) +static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid) { int ret; struct sel_netif *netif; @@ -140,7 +143,7 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid) /* NOTE: we always use init's network namespace since we don't * currently support containers */ - dev = dev_get_by_index(&init_net, ifindex); + dev = dev_get_by_index(ns, ifindex); if (unlikely(dev == NULL)) { printk(KERN_WARNING "SELinux: failure in sel_netif_sid_slow()," @@ -149,7 +152,7 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid) } spin_lock_bh(&sel_netif_lock); - netif = sel_netif_find(ifindex); + netif = sel_netif_find(ns, ifindex); if (netif != NULL) { *sid = netif->nsec.sid; ret = 0; @@ -163,6 +166,7 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid) ret = security_netif_sid(dev->name, &new->nsec.sid); if (ret != 0) goto out; + new->nsec.ns = ns; new->nsec.ifindex = ifindex; ret = sel_netif_insert(new); if (ret != 0) @@ -184,6 +188,7 @@ out: /** * sel_netif_sid - Lookup the SID of a network interface + * @ns: the network namespace * @ifindex: the network interface * @sid: interface SID * @@ -195,12 +200,12 @@ out: * on failure. * */ -int sel_netif_sid(int ifindex, u32 *sid) +int sel_netif_sid(struct net *ns, int ifindex, u32 *sid) { struct sel_netif *netif; rcu_read_lock(); - netif = sel_netif_find(ifindex); + netif = sel_netif_find(ns, ifindex); if (likely(netif != NULL)) { *sid = netif->nsec.sid; rcu_read_unlock(); @@ -208,11 +213,12 @@ int sel_netif_sid(int ifindex, u32 *sid) } rcu_read_unlock(); - return sel_netif_sid_slow(ifindex, sid); + return sel_netif_sid_slow(ns, ifindex, sid); } /** * sel_netif_kill - Remove an entry from the network interface table + * @ns: the network namespace * @ifindex: the network interface * * Description: @@ -220,13 +226,13 @@ int sel_netif_sid(int ifindex, u32 *sid) * table if it exists. * */ -static void sel_netif_kill(int ifindex) +static void sel_netif_kill(const struct net *ns, int ifindex) { struct sel_netif *netif; rcu_read_lock(); spin_lock_bh(&sel_netif_lock); - netif = sel_netif_find(ifindex); + netif = sel_netif_find(ns, ifindex); if (netif) sel_netif_destroy(netif); spin_unlock_bh(&sel_netif_lock); @@ -240,7 +246,7 @@ static void sel_netif_kill(int ifindex) * Remove all entries from the network interface table. * */ -static void sel_netif_flush(void) +void sel_netif_flush(void) { int idx; struct sel_netif *netif; @@ -252,25 +258,13 @@ static void sel_netif_flush(void) spin_unlock_bh(&sel_netif_lock); } -static int sel_netif_avc_callback(u32 event) -{ - if (event == AVC_CALLBACK_RESET) { - sel_netif_flush(); - synchronize_net(); - } - return 0; -} - static int sel_netif_netdev_notifier_handler(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *dev = netdev_notifier_info_to_dev(ptr); - if (dev_net(dev) != &init_net) - return NOTIFY_DONE; - if (event == NETDEV_DOWN) - sel_netif_kill(dev->ifindex); + sel_netif_kill(dev_net(dev), dev->ifindex); return NOTIFY_DONE; } @@ -291,10 +285,6 @@ static __init int sel_netif_init(void) register_netdevice_notifier(&sel_netif_netdev_notifier); - err = avc_add_callback(sel_netif_avc_callback, AVC_CALLBACK_RESET); - if (err) - panic("avc_add_callback() failed, error %d\n", err); - return err; } diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c index 03a72c32afd7..ddf315260839 100644 --- a/security/selinux/netnode.c +++ b/security/selinux/netnode.c @@ -283,7 +283,7 @@ int sel_netnode_sid(void *addr, u16 family, u32 *sid) * Remove all entries from the network address table. * */ -static void sel_netnode_flush(void) +void sel_netnode_flush(void) { unsigned int idx; struct sel_netnode *node, *node_tmp; @@ -300,15 +300,6 @@ static void sel_netnode_flush(void) spin_unlock_bh(&sel_netnode_lock); } -static int sel_netnode_avc_callback(u32 event) -{ - if (event == AVC_CALLBACK_RESET) { - sel_netnode_flush(); - synchronize_net(); - } - return 0; -} - static __init int sel_netnode_init(void) { int iter; @@ -322,10 +313,6 @@ static __init int sel_netnode_init(void) sel_netnode_hash[iter].size = 0; } - ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET); - if (ret != 0) - panic("avc_add_callback() failed, error %d\n", ret); - return ret; } diff --git a/security/selinux/netport.c b/security/selinux/netport.c index d35379781c2c..73ac6784d091 100644 --- a/security/selinux/netport.c +++ b/security/selinux/netport.c @@ -217,7 +217,7 @@ int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid) * Remove all entries from the network address table. * */ -static void sel_netport_flush(void) +void sel_netport_flush(void) { unsigned int idx; struct sel_netport *port, *port_tmp; @@ -234,15 +234,6 @@ static void sel_netport_flush(void) spin_unlock_bh(&sel_netport_lock); } -static int sel_netport_avc_callback(u32 event) -{ - if (event == AVC_CALLBACK_RESET) { - sel_netport_flush(); - synchronize_net(); - } - return 0; -} - static __init int sel_netport_init(void) { int iter; @@ -256,10 +247,6 @@ static __init int sel_netport_init(void) sel_netport_hash[iter].size = 0; } - ret = avc_add_callback(sel_netport_avc_callback, AVC_CALLBACK_RESET); - if (ret != 0) - panic("avc_add_callback() failed, error %d\n", ret); - return ret; } |