diff options
Diffstat (limited to 'arch/arm64')
182 files changed, 12804 insertions, 2199 deletions
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index c04454876bc..839f48c26ef 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1,6 +1,10 @@  config ARM64  	def_bool y  	select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE +	select ARCH_HAS_OPP +	select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST +	select ARCH_USE_CMPXCHG_LOCKREF +	select ARCH_SUPPORTS_ATOMIC_RMW  	select ARCH_WANT_OPTIONAL_GPIOLIB  	select ARCH_WANT_COMPAT_IPC_PARSE_VERSION  	select ARCH_WANT_FRAME_POINTERS @@ -10,27 +14,49 @@ config ARM64  	select BUILDTIME_EXTABLE_SORT  	select CLONE_BACKWARDS  	select COMMON_CLK +	select CPU_PM if (SUSPEND || CPU_IDLE) +	select DCACHE_WORD_ACCESS  	select GENERIC_CLOCKEVENTS +	select GENERIC_CLOCKEVENTS_BROADCAST if SMP +	select GENERIC_CPU_AUTOPROBE +	select GENERIC_EARLY_IOREMAP  	select GENERIC_IOMAP  	select GENERIC_IRQ_PROBE  	select GENERIC_IRQ_SHOW +	select GENERIC_SCHED_CLOCK  	select GENERIC_SMP_IDLE_THREAD +	select GENERIC_STRNCPY_FROM_USER +	select GENERIC_STRNLEN_USER  	select GENERIC_TIME_VSYSCALL  	select HARDIRQS_SW_RESEND +	select HAVE_ARCH_JUMP_LABEL +	select HAVE_ARCH_KGDB  	select HAVE_ARCH_TRACEHOOK +	select HAVE_C_RECORDMCOUNT  	select HAVE_DEBUG_BUGVERBOSE  	select HAVE_DEBUG_KMEMLEAK  	select HAVE_DMA_API_DEBUG  	select HAVE_DMA_ATTRS +	select HAVE_DMA_CONTIGUOUS +	select HAVE_DYNAMIC_FTRACE +	select HAVE_EFFICIENT_UNALIGNED_ACCESS +	select HAVE_FTRACE_MCOUNT_RECORD +	select HAVE_FUNCTION_TRACER +	select HAVE_FUNCTION_GRAPH_TRACER  	select HAVE_GENERIC_DMA_COHERENT  	select HAVE_HW_BREAKPOINT if PERF_EVENTS  	select HAVE_MEMBLOCK +	select HAVE_PATA_PLATFORM  	select HAVE_PERF_EVENTS +	select HAVE_PERF_REGS +	select HAVE_PERF_USER_STACK_DUMP +	select HAVE_SYSCALL_TRACEPOINTS  	select IRQ_DOMAIN  	select MODULES_USE_ELF_RELA  	select NO_BOOTMEM  	select OF  	select OF_EARLY_FLATTREE +	select OF_RESERVED_MEM  	select PERF_USE_VMALLOC  	select POWER_RESET  	select POWER_SUPPLY @@ -49,7 +75,7 @@ config ARCH_PHYS_ADDR_T_64BIT  config MMU  	def_bool y -config NO_IOPORT +config NO_IOPORT_MAP  	def_bool y  config STACKTRACE_SUPPORT @@ -61,11 +87,7 @@ config LOCKDEP_SUPPORT  config TRACE_IRQFLAGS_SUPPORT  	def_bool y -config GENERIC_LOCKBREAK -	def_bool y -	depends on SMP && PREEMPT - -config RWSEM_GENERIC_SPINLOCK +config RWSEM_XCHGADD_ALGORITHM  	def_bool y  config GENERIC_HWEIGHT @@ -77,7 +99,7 @@ config GENERIC_CSUM  config GENERIC_CALIBRATE_DELAY  	def_bool y -config ZONE_DMA32 +config ZONE_DMA  	def_bool y  config ARCH_DMA_ADDR_T_64BIT @@ -98,6 +120,9 @@ config IOMMU_HELPER  config KERNEL_MODE_NEON  	def_bool y +config FIX_EARLYCON_MEM +	def_bool y +  source "init/Kconfig"  source "kernel/Kconfig.freezer" @@ -138,9 +163,13 @@ config ARM64_64K_PAGES  	  look-up. AArch32 emulation is not available when this feature  	  is enabled. +config CPU_BIG_ENDIAN +       bool "Build big-endian kernel" +       help +         Say Y if you plan on running a kernel in big-endian mode. +  config SMP  	bool "Symmetric Multi-Processing" -	select USE_GENERIC_SMP_HELPERS  	help  	  This enables support for systems with more than one CPU.  If  	  you say N here, the kernel will run on single and @@ -152,13 +181,35 @@ config SMP  	  If you don't know what to do here, say N. +config SCHED_MC +	bool "Multi-core scheduler support" +	depends on SMP +	help +	  Multi-core scheduler support improves the CPU scheduler's decision +	  making when dealing with multi-core CPU chips at a cost of slightly +	  increased overhead in some places. If unsure say N here. + +config SCHED_SMT +	bool "SMT scheduler support" +	depends on SMP +	help +	  Improves the CPU scheduler's decision making when dealing with +	  MultiThreading at a cost of slightly increased overhead in some +	  places. If unsure say N here. +  config NR_CPUS  	int "Maximum number of CPUs (2-32)"  	range 2 32  	depends on SMP  	# These have to remain sorted largest to smallest -	default "8" if ARCH_XGENE -	default "4" +	default "8" + +config HOTPLUG_CPU +	bool "Support for hot-pluggable CPUs" +	depends on SMP +	help +	  Say Y here to experiment with turning CPUs off and on.  CPUs +	  can be controlled through /sys/devices/system/cpu.  source kernel/Kconfig.preempt @@ -202,6 +253,9 @@ config ARCH_WANT_HUGE_PMD_SHARE  config HAVE_ARCH_TRANSPARENT_HUGEPAGE  	def_bool y +config ARCH_HAS_CACHE_LINE_SIZE +	def_bool y +  source "mm/Kconfig"  config XEN_DOM0 @@ -211,6 +265,7 @@ config XEN_DOM0  config XEN  	bool "Xen guest support on ARM64 (EXPERIMENTAL)"  	depends on ARM64 && OF +	select SWIOTLB_XEN  	help  	  Say Y if you want to run Linux in a Virtual Machine on Xen on ARM64. @@ -239,6 +294,20 @@ config CMDLINE_FORCE  	  This is useful if you cannot or don't want to change the  	  command-line options your boot loader passes to the kernel. +config EFI +	bool "UEFI runtime support" +	depends on OF && !CPU_BIG_ENDIAN +	select LIBFDT +	select UCS2_STRING +	select EFI_PARAMS_FROM_FDT +	default y +	help +	  This option provides support for runtime services provided +	  by UEFI firmware (such as non-volatile variables, realtime +          clock, and platform reset). A UEFI stub is also provided to +	  allow the kernel to be booted as an EFI application. This +	  is only useful on systems that have UEFI firmware. +  endmenu  menu "Userspace binary formats" @@ -266,10 +335,32 @@ config SYSVIPC_COMPAT  endmenu +menu "Power management options" + +source "kernel/power/Kconfig" + +config ARCH_SUSPEND_POSSIBLE +	def_bool y + +config ARM64_CPU_SUSPEND +	def_bool PM_SLEEP + +endmenu + +menu "CPU Power Management" + +source "drivers/cpuidle/Kconfig" + +source "drivers/cpufreq/Kconfig" + +endmenu +  source "net/Kconfig"  source "drivers/Kconfig" +source "drivers/firmware/Kconfig" +  source "fs/Kconfig"  source "arch/arm64/kvm/Kconfig" @@ -279,5 +370,8 @@ source "arch/arm64/Kconfig.debug"  source "security/Kconfig"  source "crypto/Kconfig" +if CRYPTO +source "arch/arm64/crypto/Kconfig" +endif  source "lib/Kconfig" diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug index 1a6bfe954d4..1c1b7562984 100644 --- a/arch/arm64/Kconfig.debug +++ b/arch/arm64/Kconfig.debug @@ -6,21 +6,19 @@ config FRAME_POINTER  	bool  	default y -config DEBUG_STACK_USAGE -	bool "Enable stack utilization instrumentation" -	depends on DEBUG_KERNEL +config STRICT_DEVMEM +	bool "Filter access to /dev/mem" +	depends on MMU  	help -	  Enables the display of the minimum amount of free stack which each -	  task has ever had available in the sysrq-T output. +	  If this option is disabled, you allow userspace (root) access to all +	  of memory, including kernel and userspace memory. Accidental +	  access to this is obviously disastrous, but specific access can +	  be used by people debugging the kernel. -config EARLY_PRINTK -	bool "Early printk support" -	default y -	help -	  Say Y here if you want to have an early console using the -	  earlyprintk=<name>[,<addr>][,<options>] kernel parameter. It -	  is assumed that the early console device has been initialised -	  by the boot loader prior to starting the Linux kernel. +	  If this option is switched on, the /dev/mem file only allows +	  userspace access to memory mapped peripherals. + +	  If in doubt, say Y.  config PID_IN_CONTEXTIDR  	bool "Write the current PID to the CONTEXTIDR register" diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index d90cf79f233..8185a913c5e 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -20,9 +20,15 @@ LIBGCC 		:= $(shell $(CC) $(KBUILD_CFLAGS) -print-libgcc-file-name)  KBUILD_DEFCONFIG := defconfig  KBUILD_CFLAGS	+= -mgeneral-regs-only +ifeq ($(CONFIG_CPU_BIG_ENDIAN), y) +KBUILD_CPPFLAGS	+= -mbig-endian +AS		+= -EB +LD		+= -EB +else  KBUILD_CPPFLAGS	+= -mlittle-endian  AS		+= -EL  LD		+= -EL +endif  comma = , @@ -39,6 +45,7 @@ export	TEXT_OFFSET GZFLAGS  core-y		+= arch/arm64/kernel/ arch/arm64/mm/  core-$(CONFIG_KVM) += arch/arm64/kvm/  core-$(CONFIG_XEN) += arch/arm64/xen/ +core-$(CONFIG_CRYPTO) += arch/arm64/crypto/  libs-y		:= arch/arm64/lib/ $(libs-y)  libs-y		+= $(LIBGCC) diff --git a/arch/arm64/boot/dts/apm-mustang.dts b/arch/arm64/boot/dts/apm-mustang.dts index 1247ca1200b..6541962f5d7 100644 --- a/arch/arm64/boot/dts/apm-mustang.dts +++ b/arch/arm64/boot/dts/apm-mustang.dts @@ -24,3 +24,7 @@  		reg = < 0x1 0x00000000 0x0 0x80000000 >; /* Updated by bootloader */  	};  }; + +&serial0 { +	status = "ok"; +}; diff --git a/arch/arm64/boot/dts/apm-storm.dtsi b/arch/arm64/boot/dts/apm-storm.dtsi index bfdc5783492..40aa96ce13c 100644 --- a/arch/arm64/boot/dts/apm-storm.dtsi +++ b/arch/arm64/boot/dts/apm-storm.dtsi @@ -103,14 +103,299 @@  		#size-cells = <2>;  		ranges; +		clocks { +			#address-cells = <2>; +			#size-cells = <2>; +			ranges; +			refclk: refclk { +				compatible = "fixed-clock"; +				#clock-cells = <1>; +				clock-frequency = <100000000>; +				clock-output-names = "refclk"; +			}; + +			pcppll: pcppll@17000100 { +				compatible = "apm,xgene-pcppll-clock"; +				#clock-cells = <1>; +				clocks = <&refclk 0>; +				clock-names = "pcppll"; +				reg = <0x0 0x17000100 0x0 0x1000>; +				clock-output-names = "pcppll"; +				type = <0>; +			}; + +			socpll: socpll@17000120 { +				compatible = "apm,xgene-socpll-clock"; +				#clock-cells = <1>; +				clocks = <&refclk 0>; +				clock-names = "socpll"; +				reg = <0x0 0x17000120 0x0 0x1000>; +				clock-output-names = "socpll"; +				type = <1>; +			}; + +			socplldiv2: socplldiv2  { +				compatible = "fixed-factor-clock"; +				#clock-cells = <1>; +				clocks = <&socpll 0>; +				clock-names = "socplldiv2"; +				clock-mult = <1>; +				clock-div = <2>; +				clock-output-names = "socplldiv2"; +			}; + +			qmlclk: qmlclk { +				compatible = "apm,xgene-device-clock"; +				#clock-cells = <1>; +				clocks = <&socplldiv2 0>; +				clock-names = "qmlclk"; +				reg = <0x0 0x1703C000 0x0 0x1000>; +				reg-names = "csr-reg"; +				clock-output-names = "qmlclk"; +			}; + +			ethclk: ethclk { +				compatible = "apm,xgene-device-clock"; +				#clock-cells = <1>; +				clocks = <&socplldiv2 0>; +				clock-names = "ethclk"; +				reg = <0x0 0x17000000 0x0 0x1000>; +				reg-names = "div-reg"; +				divider-offset = <0x238>; +				divider-width = <0x9>; +				divider-shift = <0x0>; +				clock-output-names = "ethclk"; +			}; + +			eth8clk: eth8clk { +				compatible = "apm,xgene-device-clock"; +				#clock-cells = <1>; +				clocks = <ðclk 0>; +				clock-names = "eth8clk"; +				reg = <0x0 0x1702C000 0x0 0x1000>; +				reg-names = "csr-reg"; +				clock-output-names = "eth8clk"; +			}; + +			sataphy1clk: sataphy1clk@1f21c000 { +				compatible = "apm,xgene-device-clock"; +				#clock-cells = <1>; +				clocks = <&socplldiv2 0>; +				reg = <0x0 0x1f21c000 0x0 0x1000>; +				reg-names = "csr-reg"; +				clock-output-names = "sataphy1clk"; +				status = "disabled"; +				csr-offset = <0x4>; +				csr-mask = <0x00>; +				enable-offset = <0x0>; +				enable-mask = <0x06>; +			}; + +			sataphy2clk: sataphy1clk@1f22c000 { +				compatible = "apm,xgene-device-clock"; +				#clock-cells = <1>; +				clocks = <&socplldiv2 0>; +				reg = <0x0 0x1f22c000 0x0 0x1000>; +				reg-names = "csr-reg"; +				clock-output-names = "sataphy2clk"; +				status = "ok"; +				csr-offset = <0x4>; +				csr-mask = <0x3a>; +				enable-offset = <0x0>; +				enable-mask = <0x06>; +			}; + +			sataphy3clk: sataphy1clk@1f23c000 { +				compatible = "apm,xgene-device-clock"; +				#clock-cells = <1>; +				clocks = <&socplldiv2 0>; +				reg = <0x0 0x1f23c000 0x0 0x1000>; +				reg-names = "csr-reg"; +				clock-output-names = "sataphy3clk"; +				status = "ok"; +				csr-offset = <0x4>; +				csr-mask = <0x3a>; +				enable-offset = <0x0>; +				enable-mask = <0x06>; +			}; + +			sata01clk: sata01clk@1f21c000 { +				compatible = "apm,xgene-device-clock"; +				#clock-cells = <1>; +				clocks = <&socplldiv2 0>; +				reg = <0x0 0x1f21c000 0x0 0x1000>; +				reg-names = "csr-reg"; +				clock-output-names = "sata01clk"; +				csr-offset = <0x4>; +				csr-mask = <0x05>; +				enable-offset = <0x0>; +				enable-mask = <0x39>; +			}; + +			sata23clk: sata23clk@1f22c000 { +				compatible = "apm,xgene-device-clock"; +				#clock-cells = <1>; +				clocks = <&socplldiv2 0>; +				reg = <0x0 0x1f22c000 0x0 0x1000>; +				reg-names = "csr-reg"; +				clock-output-names = "sata23clk"; +				csr-offset = <0x4>; +				csr-mask = <0x05>; +				enable-offset = <0x0>; +				enable-mask = <0x39>; +			}; + +			sata45clk: sata45clk@1f23c000 { +				compatible = "apm,xgene-device-clock"; +				#clock-cells = <1>; +				clocks = <&socplldiv2 0>; +				reg = <0x0 0x1f23c000 0x0 0x1000>; +				reg-names = "csr-reg"; +				clock-output-names = "sata45clk"; +				csr-offset = <0x4>; +				csr-mask = <0x05>; +				enable-offset = <0x0>; +				enable-mask = <0x39>; +			}; + +			rtcclk: rtcclk@17000000 { +				compatible = "apm,xgene-device-clock"; +				#clock-cells = <1>; +				clocks = <&socplldiv2 0>; +				reg = <0x0 0x17000000 0x0 0x2000>; +				reg-names = "csr-reg"; +				csr-offset = <0xc>; +				csr-mask = <0x2>; +				enable-offset = <0x10>; +				enable-mask = <0x2>; +				clock-output-names = "rtcclk"; +			}; +		}; +  		serial0: serial@1c020000 { +			status = "disabled";  			device_type = "serial"; -			compatible = "ns16550"; +			compatible = "ns16550a";  			reg = <0 0x1c020000 0x0 0x1000>;  			reg-shift = <2>;  			clock-frequency = <10000000>; /* Updated by bootloader */  			interrupt-parent = <&gic>;  			interrupts = <0x0 0x4c 0x4>;  		}; + +		serial1: serial@1c021000 { +			status = "disabled"; +			device_type = "serial"; +			compatible = "ns16550a"; +			reg = <0 0x1c021000 0x0 0x1000>; +			reg-shift = <2>; +			clock-frequency = <10000000>; /* Updated by bootloader */ +			interrupt-parent = <&gic>; +			interrupts = <0x0 0x4d 0x4>; +		}; + +		serial2: serial@1c022000 { +			status = "disabled"; +			device_type = "serial"; +			compatible = "ns16550a"; +			reg = <0 0x1c022000 0x0 0x1000>; +			reg-shift = <2>; +			clock-frequency = <10000000>; /* Updated by bootloader */ +			interrupt-parent = <&gic>; +			interrupts = <0x0 0x4e 0x4>; +		}; + +		serial3: serial@1c023000 { +			status = "disabled"; +			device_type = "serial"; +			compatible = "ns16550a"; +			reg = <0 0x1c023000 0x0 0x1000>; +			reg-shift = <2>; +			clock-frequency = <10000000>; /* Updated by bootloader */ +			interrupt-parent = <&gic>; +			interrupts = <0x0 0x4f 0x4>; +		}; + +		phy1: phy@1f21a000 { +			compatible = "apm,xgene-phy"; +			reg = <0x0 0x1f21a000 0x0 0x100>; +			#phy-cells = <1>; +			clocks = <&sataphy1clk 0>; +			status = "disabled"; +			apm,tx-boost-gain = <30 30 30 30 30 30>; +			apm,tx-eye-tuning = <2 10 10 2 10 10>; +		}; + +		phy2: phy@1f22a000 { +			compatible = "apm,xgene-phy"; +			reg = <0x0 0x1f22a000 0x0 0x100>; +			#phy-cells = <1>; +			clocks = <&sataphy2clk 0>; +			status = "ok"; +			apm,tx-boost-gain = <30 30 30 30 30 30>; +			apm,tx-eye-tuning = <1 10 10 2 10 10>; +		}; + +		phy3: phy@1f23a000 { +			compatible = "apm,xgene-phy"; +			reg = <0x0 0x1f23a000 0x0 0x100>; +			#phy-cells = <1>; +			clocks = <&sataphy3clk 0>; +			status = "ok"; +			apm,tx-boost-gain = <31 31 31 31 31 31>; +			apm,tx-eye-tuning = <2 10 10 2 10 10>; +		}; + +		sata1: sata@1a000000 { +			compatible = "apm,xgene-ahci"; +			reg = <0x0 0x1a000000 0x0 0x1000>, +			      <0x0 0x1f210000 0x0 0x1000>, +			      <0x0 0x1f21d000 0x0 0x1000>, +			      <0x0 0x1f21e000 0x0 0x1000>, +			      <0x0 0x1f217000 0x0 0x1000>; +			interrupts = <0x0 0x86 0x4>; +			dma-coherent; +			status = "disabled"; +			clocks = <&sata01clk 0>; +			phys = <&phy1 0>; +			phy-names = "sata-phy"; +		}; + +		sata2: sata@1a400000 { +			compatible = "apm,xgene-ahci"; +			reg = <0x0 0x1a400000 0x0 0x1000>, +			      <0x0 0x1f220000 0x0 0x1000>, +			      <0x0 0x1f22d000 0x0 0x1000>, +			      <0x0 0x1f22e000 0x0 0x1000>, +			      <0x0 0x1f227000 0x0 0x1000>; +			interrupts = <0x0 0x87 0x4>; +			dma-coherent; +			status = "ok"; +			clocks = <&sata23clk 0>; +			phys = <&phy2 0>; +			phy-names = "sata-phy"; +		}; + +		sata3: sata@1a800000 { +			compatible = "apm,xgene-ahci"; +			reg = <0x0 0x1a800000 0x0 0x1000>, +			      <0x0 0x1f230000 0x0 0x1000>, +			      <0x0 0x1f23d000 0x0 0x1000>, +			      <0x0 0x1f23e000 0x0 0x1000>; +			interrupts = <0x0 0x88 0x4>; +			dma-coherent; +			status = "ok"; +			clocks = <&sata45clk 0>; +			phys = <&phy3 0>; +			phy-names = "sata-phy"; +		}; + +		rtc: rtc@10510000 { +			compatible = "apm,xgene-rtc"; +			reg = <0x0 0x10510000 0x0 0x400>; +			interrupts = <0x0 0x46 0x4>; +			#clock-cells = <1>; +			clocks = <&rtcclk 0>; +		};  	};  }; diff --git a/arch/arm64/boot/dts/foundation-v8.dts b/arch/arm64/boot/dts/foundation-v8.dts index 84fcc501828..4a060906809 100644 --- a/arch/arm64/boot/dts/foundation-v8.dts +++ b/arch/arm64/boot/dts/foundation-v8.dts @@ -6,6 +6,8 @@  /dts-v1/; +/memreserve/ 0x80000000 0x00010000; +  / {  	model = "Foundation-v8A";  	compatible = "arm,foundation-aarch64", "arm,vexpress"; @@ -222,7 +224,7 @@  			virtio_block@0130000 {  				compatible = "virtio,mmio"; -				reg = <0x130000 0x1000>; +				reg = <0x130000 0x200>;  				interrupts = <42>;  			};  		}; diff --git a/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi b/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi index b45e5f39f57..ac2cb241802 100644 --- a/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi +++ b/arch/arm64/boot/dts/rtsm_ve-motherboard.dtsi @@ -183,6 +183,12 @@  				clocks = <&v2m_oscclk1>, <&v2m_clk24mhz>;  				clock-names = "clcdclk", "apb_pclk";  			}; + +			virtio_block@0130000 { +				compatible = "virtio,mmio"; +				reg = <0x130000 0x200>; +				interrupts = <42>; +			};  		};  		v2m_fixed_3v3: fixedregulator@0 { @@ -194,7 +200,7 @@  		};  		mcc { -			compatible = "arm,vexpress,config-bus", "simple-bus"; +			compatible = "arm,vexpress,config-bus";  			arm,vexpress,config-bridge = <&v2m_sysreg>;  			v2m_oscclk1: osc@1 { diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 5b3e83217b0..3421f316f5d 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -1,15 +1,23 @@ -CONFIG_EXPERIMENTAL=y  # CONFIG_LOCALVERSION_AUTO is not set -# CONFIG_SWAP is not set  CONFIG_SYSVIPC=y  CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y  CONFIG_BSD_PROCESS_ACCT=y  CONFIG_BSD_PROCESS_ACCT_V3=y -CONFIG_NO_HZ=y -CONFIG_HIGH_RES_TIMERS=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y  CONFIG_IKCONFIG=y  CONFIG_IKCONFIG_PROC=y  CONFIG_LOG_BUF_SHIFT=14 +CONFIG_RESOURCE_COUNTERS=y +CONFIG_MEMCG=y +CONFIG_MEMCG_SWAP=y +CONFIG_MEMCG_KMEM=y +CONFIG_CGROUP_HUGETLB=y  # CONFIG_UTS_NS is not set  # CONFIG_IPC_NS is not set  # CONFIG_PID_NS is not set @@ -19,6 +27,7 @@ CONFIG_BLK_DEV_INITRD=y  CONFIG_KALLSYMS_ALL=y  # CONFIG_COMPAT_BRK is not set  CONFIG_PROFILING=y +CONFIG_JUMP_LABEL=y  CONFIG_MODULES=y  CONFIG_MODULE_UNLOAD=y  # CONFIG_BLK_DEV_BSG is not set @@ -26,7 +35,10 @@ CONFIG_MODULE_UNLOAD=y  CONFIG_ARCH_VEXPRESS=y  CONFIG_ARCH_XGENE=y  CONFIG_SMP=y -CONFIG_PREEMPT_VOLUNTARY=y +CONFIG_PREEMPT=y +CONFIG_KSM=y +CONFIG_TRANSPARENT_HUGEPAGE=y +CONFIG_CMA=y  CONFIG_CMDLINE="console=ttyAMA0"  # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set  CONFIG_COMPAT=y @@ -42,51 +54,77 @@ CONFIG_IP_PNP_BOOTP=y  # CONFIG_WIRELESS is not set  CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"  CONFIG_DEVTMPFS=y -# CONFIG_BLK_DEV is not set -CONFIG_SCSI=y +CONFIG_DMA_CMA=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_VIRTIO_BLK=y  # CONFIG_SCSI_PROC_FS is not set  CONFIG_BLK_DEV_SD=y  # CONFIG_SCSI_LOWLEVEL is not set +CONFIG_ATA=y +CONFIG_PATA_PLATFORM=y +CONFIG_PATA_OF_PLATFORM=y  CONFIG_NETDEVICES=y -CONFIG_MII=y +CONFIG_TUN=y  CONFIG_SMC91X=y +CONFIG_SMSC911X=y  # CONFIG_WLAN is not set  CONFIG_INPUT_EVDEV=y -# CONFIG_SERIO_I8042 is not set  # CONFIG_SERIO_SERPORT is not set  CONFIG_LEGACY_PTY_COUNT=16  CONFIG_SERIAL_8250=y  CONFIG_SERIAL_8250_CONSOLE=y -CONFIG_SERIAL_OF_PLATFORM=y  CONFIG_SERIAL_AMBA_PL011=y  CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_OF_PLATFORM=y  # CONFIG_HW_RANDOM is not set  # CONFIG_HWMON is not set +CONFIG_REGULATOR=y +CONFIG_REGULATOR_FIXED_VOLTAGE=y  CONFIG_FB=y -# CONFIG_VGA_CONSOLE is not set  CONFIG_FRAMEBUFFER_CONSOLE=y  CONFIG_LOGO=y  # CONFIG_LOGO_LINUX_MONO is not set  # CONFIG_LOGO_LINUX_VGA16 is not set -# CONFIG_USB_SUPPORT is not set +CONFIG_USB=y +CONFIG_USB_ISP1760_HCD=y +CONFIG_USB_STORAGE=y +CONFIG_MMC=y +CONFIG_MMC_ARMMMCI=y +CONFIG_VIRTIO_MMIO=y  # CONFIG_IOMMU_SUPPORT is not set  CONFIG_EXT2_FS=y  CONFIG_EXT3_FS=y  # CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set  # CONFIG_EXT3_FS_XATTR is not set +CONFIG_EXT4_FS=y +CONFIG_FANOTIFY=y +CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y  CONFIG_FUSE_FS=y  CONFIG_CUSE=y  CONFIG_VFAT_FS=y  CONFIG_TMPFS=y +CONFIG_HUGETLBFS=y  # CONFIG_MISC_FILESYSTEMS is not set  CONFIG_NFS_FS=y  CONFIG_ROOT_NFS=y  CONFIG_NLS_CODEPAGE_437=y  CONFIG_NLS_ISO8859_1=y -CONFIG_MAGIC_SYSRQ=y +CONFIG_VIRTUALIZATION=y +CONFIG_KVM=y +CONFIG_DEBUG_INFO=y  CONFIG_DEBUG_FS=y +CONFIG_MAGIC_SYSRQ=y  CONFIG_DEBUG_KERNEL=y +CONFIG_LOCKUP_DETECTOR=y  # CONFIG_SCHED_DEBUG is not set -CONFIG_DEBUG_INFO=y  # CONFIG_FTRACE is not set -CONFIG_ATOMIC64_SELFTEST=y +CONFIG_SECURITY=y +CONFIG_CRYPTO_ANSI_CPRNG=y +CONFIG_ARM64_CRYPTO=y +CONFIG_CRYPTO_SHA1_ARM64_CE=y +CONFIG_CRYPTO_SHA2_ARM64_CE=y +CONFIG_CRYPTO_GHASH_ARM64_CE=y +CONFIG_CRYPTO_AES_ARM64_CE=y +CONFIG_CRYPTO_AES_ARM64_CE_CCM=y +CONFIG_CRYPTO_AES_ARM64_CE_BLK=y +CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y diff --git a/arch/arm64/crypto/Kconfig b/arch/arm64/crypto/Kconfig new file mode 100644 index 00000000000..5562652c531 --- /dev/null +++ b/arch/arm64/crypto/Kconfig @@ -0,0 +1,53 @@ + +menuconfig ARM64_CRYPTO +	bool "ARM64 Accelerated Cryptographic Algorithms" +	depends on ARM64 +	help +	  Say Y here to choose from a selection of cryptographic algorithms +	  implemented using ARM64 specific CPU features or instructions. + +if ARM64_CRYPTO + +config CRYPTO_SHA1_ARM64_CE +	tristate "SHA-1 digest algorithm (ARMv8 Crypto Extensions)" +	depends on ARM64 && KERNEL_MODE_NEON +	select CRYPTO_HASH + +config CRYPTO_SHA2_ARM64_CE +	tristate "SHA-224/SHA-256 digest algorithm (ARMv8 Crypto Extensions)" +	depends on ARM64 && KERNEL_MODE_NEON +	select CRYPTO_HASH + +config CRYPTO_GHASH_ARM64_CE +	tristate "GHASH (for GCM chaining mode) using ARMv8 Crypto Extensions" +	depends on ARM64 && KERNEL_MODE_NEON +	select CRYPTO_HASH + +config CRYPTO_AES_ARM64_CE +	tristate "AES core cipher using ARMv8 Crypto Extensions" +	depends on ARM64 && KERNEL_MODE_NEON +	select CRYPTO_ALGAPI +	select CRYPTO_AES + +config CRYPTO_AES_ARM64_CE_CCM +	tristate "AES in CCM mode using ARMv8 Crypto Extensions" +	depends on ARM64 && KERNEL_MODE_NEON +	select CRYPTO_ALGAPI +	select CRYPTO_AES +	select CRYPTO_AEAD + +config CRYPTO_AES_ARM64_CE_BLK +	tristate "AES in ECB/CBC/CTR/XTS modes using ARMv8 Crypto Extensions" +	depends on ARM64 && KERNEL_MODE_NEON +	select CRYPTO_BLKCIPHER +	select CRYPTO_AES +	select CRYPTO_ABLK_HELPER + +config CRYPTO_AES_ARM64_NEON_BLK +	tristate "AES in ECB/CBC/CTR/XTS modes using NEON instructions" +	depends on ARM64 && KERNEL_MODE_NEON +	select CRYPTO_BLKCIPHER +	select CRYPTO_AES +	select CRYPTO_ABLK_HELPER + +endif diff --git a/arch/arm64/crypto/Makefile b/arch/arm64/crypto/Makefile new file mode 100644 index 00000000000..2070a56ecc4 --- /dev/null +++ b/arch/arm64/crypto/Makefile @@ -0,0 +1,38 @@ +# +# linux/arch/arm64/crypto/Makefile +# +# Copyright (C) 2014 Linaro Ltd <ard.biesheuvel@linaro.org> +# +# 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. +# + +obj-$(CONFIG_CRYPTO_SHA1_ARM64_CE) += sha1-ce.o +sha1-ce-y := sha1-ce-glue.o sha1-ce-core.o + +obj-$(CONFIG_CRYPTO_SHA2_ARM64_CE) += sha2-ce.o +sha2-ce-y := sha2-ce-glue.o sha2-ce-core.o + +obj-$(CONFIG_CRYPTO_GHASH_ARM64_CE) += ghash-ce.o +ghash-ce-y := ghash-ce-glue.o ghash-ce-core.o + +obj-$(CONFIG_CRYPTO_AES_ARM64_CE) += aes-ce-cipher.o +CFLAGS_aes-ce-cipher.o += -march=armv8-a+crypto + +obj-$(CONFIG_CRYPTO_AES_ARM64_CE_CCM) += aes-ce-ccm.o +aes-ce-ccm-y := aes-ce-ccm-glue.o aes-ce-ccm-core.o + +obj-$(CONFIG_CRYPTO_AES_ARM64_CE_BLK) += aes-ce-blk.o +aes-ce-blk-y := aes-glue-ce.o aes-ce.o + +obj-$(CONFIG_CRYPTO_AES_ARM64_NEON_BLK) += aes-neon-blk.o +aes-neon-blk-y := aes-glue-neon.o aes-neon.o + +AFLAGS_aes-ce.o		:= -DINTERLEAVE=2 -DINTERLEAVE_INLINE +AFLAGS_aes-neon.o	:= -DINTERLEAVE=4 + +CFLAGS_aes-glue-ce.o	:= -DUSE_V8_CRYPTO_EXTENSIONS + +$(obj)/aes-glue-%.o: $(src)/aes-glue.c FORCE +	$(call if_changed_dep,cc_o_c) diff --git a/arch/arm64/crypto/aes-ce-ccm-core.S b/arch/arm64/crypto/aes-ce-ccm-core.S new file mode 100644 index 00000000000..432e4841cd8 --- /dev/null +++ b/arch/arm64/crypto/aes-ce-ccm-core.S @@ -0,0 +1,222 @@ +/* + * aesce-ccm-core.S - AES-CCM transform for ARMv8 with Crypto Extensions + * + * Copyright (C) 2013 - 2014 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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/linkage.h> + +	.text +	.arch	armv8-a+crypto + +	/* +	 * void ce_aes_ccm_auth_data(u8 mac[], u8 const in[], u32 abytes, +	 *			     u32 *macp, u8 const rk[], u32 rounds); +	 */ +ENTRY(ce_aes_ccm_auth_data) +	ldr	w8, [x3]			/* leftover from prev round? */ +	ld1	{v0.2d}, [x0]			/* load mac */ +	cbz	w8, 1f +	sub	w8, w8, #16 +	eor	v1.16b, v1.16b, v1.16b +0:	ldrb	w7, [x1], #1			/* get 1 byte of input */ +	subs	w2, w2, #1 +	add	w8, w8, #1 +	ins	v1.b[0], w7 +	ext	v1.16b, v1.16b, v1.16b, #1	/* rotate in the input bytes */ +	beq	8f				/* out of input? */ +	cbnz	w8, 0b +	eor	v0.16b, v0.16b, v1.16b +1:	ld1	{v3.2d}, [x4]			/* load first round key */ +	prfm	pldl1strm, [x1] +	cmp	w5, #12				/* which key size? */ +	add	x6, x4, #16 +	sub	w7, w5, #2			/* modified # of rounds */ +	bmi	2f +	bne	5f +	mov	v5.16b, v3.16b +	b	4f +2:	mov	v4.16b, v3.16b +	ld1	{v5.2d}, [x6], #16		/* load 2nd round key */ +3:	aese	v0.16b, v4.16b +	aesmc	v0.16b, v0.16b +4:	ld1	{v3.2d}, [x6], #16		/* load next round key */ +	aese	v0.16b, v5.16b +	aesmc	v0.16b, v0.16b +5:	ld1	{v4.2d}, [x6], #16		/* load next round key */ +	subs	w7, w7, #3 +	aese	v0.16b, v3.16b +	aesmc	v0.16b, v0.16b +	ld1	{v5.2d}, [x6], #16		/* load next round key */ +	bpl	3b +	aese	v0.16b, v4.16b +	subs	w2, w2, #16			/* last data? */ +	eor	v0.16b, v0.16b, v5.16b		/* final round */ +	bmi	6f +	ld1	{v1.16b}, [x1], #16		/* load next input block */ +	eor	v0.16b, v0.16b, v1.16b		/* xor with mac */ +	bne	1b +6:	st1	{v0.2d}, [x0]			/* store mac */ +	beq	10f +	adds	w2, w2, #16 +	beq	10f +	mov	w8, w2 +7:	ldrb	w7, [x1], #1 +	umov	w6, v0.b[0] +	eor	w6, w6, w7 +	strb	w6, [x0], #1 +	subs	w2, w2, #1 +	beq	10f +	ext	v0.16b, v0.16b, v0.16b, #1	/* rotate out the mac bytes */ +	b	7b +8:	mov	w7, w8 +	add	w8, w8, #16 +9:	ext	v1.16b, v1.16b, v1.16b, #1 +	adds	w7, w7, #1 +	bne	9b +	eor	v0.16b, v0.16b, v1.16b +	st1	{v0.2d}, [x0] +10:	str	w8, [x3] +	ret +ENDPROC(ce_aes_ccm_auth_data) + +	/* +	 * void ce_aes_ccm_final(u8 mac[], u8 const ctr[], u8 const rk[], +	 * 			 u32 rounds); +	 */ +ENTRY(ce_aes_ccm_final) +	ld1	{v3.2d}, [x2], #16		/* load first round key */ +	ld1	{v0.2d}, [x0]			/* load mac */ +	cmp	w3, #12				/* which key size? */ +	sub	w3, w3, #2			/* modified # of rounds */ +	ld1	{v1.2d}, [x1]			/* load 1st ctriv */ +	bmi	0f +	bne	3f +	mov	v5.16b, v3.16b +	b	2f +0:	mov	v4.16b, v3.16b +1:	ld1	{v5.2d}, [x2], #16		/* load next round key */ +	aese	v0.16b, v4.16b +	aese	v1.16b, v4.16b +	aesmc	v0.16b, v0.16b +	aesmc	v1.16b, v1.16b +2:	ld1	{v3.2d}, [x2], #16		/* load next round key */ +	aese	v0.16b, v5.16b +	aese	v1.16b, v5.16b +	aesmc	v0.16b, v0.16b +	aesmc	v1.16b, v1.16b +3:	ld1	{v4.2d}, [x2], #16		/* load next round key */ +	subs	w3, w3, #3 +	aese	v0.16b, v3.16b +	aese	v1.16b, v3.16b +	aesmc	v0.16b, v0.16b +	aesmc	v1.16b, v1.16b +	bpl	1b +	aese	v0.16b, v4.16b +	aese	v1.16b, v4.16b +	/* final round key cancels out */ +	eor	v0.16b, v0.16b, v1.16b		/* en-/decrypt the mac */ +	st1	{v0.2d}, [x0]			/* store result */ +	ret +ENDPROC(ce_aes_ccm_final) + +	.macro	aes_ccm_do_crypt,enc +	ldr	x8, [x6, #8]			/* load lower ctr */ +	ld1	{v0.2d}, [x5]			/* load mac */ +	rev	x8, x8				/* keep swabbed ctr in reg */ +0:	/* outer loop */ +	ld1	{v1.1d}, [x6]			/* load upper ctr */ +	prfm	pldl1strm, [x1] +	add	x8, x8, #1 +	rev	x9, x8 +	cmp	w4, #12				/* which key size? */ +	sub	w7, w4, #2			/* get modified # of rounds */ +	ins	v1.d[1], x9			/* no carry in lower ctr */ +	ld1	{v3.2d}, [x3]			/* load first round key */ +	add	x10, x3, #16 +	bmi	1f +	bne	4f +	mov	v5.16b, v3.16b +	b	3f +1:	mov	v4.16b, v3.16b +	ld1	{v5.2d}, [x10], #16		/* load 2nd round key */ +2:	/* inner loop: 3 rounds, 2x interleaved */ +	aese	v0.16b, v4.16b +	aese	v1.16b, v4.16b +	aesmc	v0.16b, v0.16b +	aesmc	v1.16b, v1.16b +3:	ld1	{v3.2d}, [x10], #16		/* load next round key */ +	aese	v0.16b, v5.16b +	aese	v1.16b, v5.16b +	aesmc	v0.16b, v0.16b +	aesmc	v1.16b, v1.16b +4:	ld1	{v4.2d}, [x10], #16		/* load next round key */ +	subs	w7, w7, #3 +	aese	v0.16b, v3.16b +	aese	v1.16b, v3.16b +	aesmc	v0.16b, v0.16b +	aesmc	v1.16b, v1.16b +	ld1	{v5.2d}, [x10], #16		/* load next round key */ +	bpl	2b +	aese	v0.16b, v4.16b +	aese	v1.16b, v4.16b +	subs	w2, w2, #16 +	bmi	6f				/* partial block? */ +	ld1	{v2.16b}, [x1], #16		/* load next input block */ +	.if	\enc == 1 +	eor	v2.16b, v2.16b, v5.16b		/* final round enc+mac */ +	eor	v1.16b, v1.16b, v2.16b		/* xor with crypted ctr */ +	.else +	eor	v2.16b, v2.16b, v1.16b		/* xor with crypted ctr */ +	eor	v1.16b, v2.16b, v5.16b		/* final round enc */ +	.endif +	eor	v0.16b, v0.16b, v2.16b		/* xor mac with pt ^ rk[last] */ +	st1	{v1.16b}, [x0], #16		/* write output block */ +	bne	0b +	rev	x8, x8 +	st1	{v0.2d}, [x5]			/* store mac */ +	str	x8, [x6, #8]			/* store lsb end of ctr (BE) */ +5:	ret + +6:	eor	v0.16b, v0.16b, v5.16b		/* final round mac */ +	eor	v1.16b, v1.16b, v5.16b		/* final round enc */ +	st1	{v0.2d}, [x5]			/* store mac */ +	add	w2, w2, #16			/* process partial tail block */ +7:	ldrb	w9, [x1], #1			/* get 1 byte of input */ +	umov	w6, v1.b[0]			/* get top crypted ctr byte */ +	umov	w7, v0.b[0]			/* get top mac byte */ +	.if	\enc == 1 +	eor	w7, w7, w9 +	eor	w9, w9, w6 +	.else +	eor	w9, w9, w6 +	eor	w7, w7, w9 +	.endif +	strb	w9, [x0], #1			/* store out byte */ +	strb	w7, [x5], #1			/* store mac byte */ +	subs	w2, w2, #1 +	beq	5b +	ext	v0.16b, v0.16b, v0.16b, #1	/* shift out mac byte */ +	ext	v1.16b, v1.16b, v1.16b, #1	/* shift out ctr byte */ +	b	7b +	.endm + +	/* +	 * void ce_aes_ccm_encrypt(u8 out[], u8 const in[], u32 cbytes, +	 * 			   u8 const rk[], u32 rounds, u8 mac[], +	 * 			   u8 ctr[]); +	 * void ce_aes_ccm_decrypt(u8 out[], u8 const in[], u32 cbytes, +	 * 			   u8 const rk[], u32 rounds, u8 mac[], +	 * 			   u8 ctr[]); +	 */ +ENTRY(ce_aes_ccm_encrypt) +	aes_ccm_do_crypt	1 +ENDPROC(ce_aes_ccm_encrypt) + +ENTRY(ce_aes_ccm_decrypt) +	aes_ccm_do_crypt	0 +ENDPROC(ce_aes_ccm_decrypt) diff --git a/arch/arm64/crypto/aes-ce-ccm-glue.c b/arch/arm64/crypto/aes-ce-ccm-glue.c new file mode 100644 index 00000000000..9e6cdde9b43 --- /dev/null +++ b/arch/arm64/crypto/aes-ce-ccm-glue.c @@ -0,0 +1,297 @@ +/* + * aes-ccm-glue.c - AES-CCM transform for ARMv8 with Crypto Extensions + * + * Copyright (C) 2013 - 2014 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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 <asm/neon.h> +#include <asm/unaligned.h> +#include <crypto/aes.h> +#include <crypto/algapi.h> +#include <crypto/scatterwalk.h> +#include <linux/crypto.h> +#include <linux/module.h> + +static int num_rounds(struct crypto_aes_ctx *ctx) +{ +	/* +	 * # of rounds specified by AES: +	 * 128 bit key		10 rounds +	 * 192 bit key		12 rounds +	 * 256 bit key		14 rounds +	 * => n byte key	=> 6 + (n/4) rounds +	 */ +	return 6 + ctx->key_length / 4; +} + +asmlinkage void ce_aes_ccm_auth_data(u8 mac[], u8 const in[], u32 abytes, +				     u32 *macp, u32 const rk[], u32 rounds); + +asmlinkage void ce_aes_ccm_encrypt(u8 out[], u8 const in[], u32 cbytes, +				   u32 const rk[], u32 rounds, u8 mac[], +				   u8 ctr[]); + +asmlinkage void ce_aes_ccm_decrypt(u8 out[], u8 const in[], u32 cbytes, +				   u32 const rk[], u32 rounds, u8 mac[], +				   u8 ctr[]); + +asmlinkage void ce_aes_ccm_final(u8 mac[], u8 const ctr[], u32 const rk[], +				 u32 rounds); + +static int ccm_setkey(struct crypto_aead *tfm, const u8 *in_key, +		      unsigned int key_len) +{ +	struct crypto_aes_ctx *ctx = crypto_aead_ctx(tfm); +	int ret; + +	ret = crypto_aes_expand_key(ctx, in_key, key_len); +	if (!ret) +		return 0; + +	tfm->base.crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; +	return -EINVAL; +} + +static int ccm_setauthsize(struct crypto_aead *tfm, unsigned int authsize) +{ +	if ((authsize & 1) || authsize < 4) +		return -EINVAL; +	return 0; +} + +static int ccm_init_mac(struct aead_request *req, u8 maciv[], u32 msglen) +{ +	struct crypto_aead *aead = crypto_aead_reqtfm(req); +	__be32 *n = (__be32 *)&maciv[AES_BLOCK_SIZE - 8]; +	u32 l = req->iv[0] + 1; + +	/* verify that CCM dimension 'L' is set correctly in the IV */ +	if (l < 2 || l > 8) +		return -EINVAL; + +	/* verify that msglen can in fact be represented in L bytes */ +	if (l < 4 && msglen >> (8 * l)) +		return -EOVERFLOW; + +	/* +	 * Even if the CCM spec allows L values of up to 8, the Linux cryptoapi +	 * uses a u32 type to represent msglen so the top 4 bytes are always 0. +	 */ +	n[0] = 0; +	n[1] = cpu_to_be32(msglen); + +	memcpy(maciv, req->iv, AES_BLOCK_SIZE - l); + +	/* +	 * Meaning of byte 0 according to CCM spec (RFC 3610/NIST 800-38C) +	 * - bits 0..2	: max # of bytes required to represent msglen, minus 1 +	 *                (already set by caller) +	 * - bits 3..5	: size of auth tag (1 => 4 bytes, 2 => 6 bytes, etc) +	 * - bit 6	: indicates presence of authenticate-only data +	 */ +	maciv[0] |= (crypto_aead_authsize(aead) - 2) << 2; +	if (req->assoclen) +		maciv[0] |= 0x40; + +	memset(&req->iv[AES_BLOCK_SIZE - l], 0, l); +	return 0; +} + +static void ccm_calculate_auth_mac(struct aead_request *req, u8 mac[]) +{ +	struct crypto_aead *aead = crypto_aead_reqtfm(req); +	struct crypto_aes_ctx *ctx = crypto_aead_ctx(aead); +	struct __packed { __be16 l; __be32 h; u16 len; } ltag; +	struct scatter_walk walk; +	u32 len = req->assoclen; +	u32 macp = 0; + +	/* prepend the AAD with a length tag */ +	if (len < 0xff00) { +		ltag.l = cpu_to_be16(len); +		ltag.len = 2; +	} else  { +		ltag.l = cpu_to_be16(0xfffe); +		put_unaligned_be32(len, <ag.h); +		ltag.len = 6; +	} + +	ce_aes_ccm_auth_data(mac, (u8 *)<ag, ltag.len, &macp, ctx->key_enc, +			     num_rounds(ctx)); +	scatterwalk_start(&walk, req->assoc); + +	do { +		u32 n = scatterwalk_clamp(&walk, len); +		u8 *p; + +		if (!n) { +			scatterwalk_start(&walk, sg_next(walk.sg)); +			n = scatterwalk_clamp(&walk, len); +		} +		p = scatterwalk_map(&walk); +		ce_aes_ccm_auth_data(mac, p, n, &macp, ctx->key_enc, +				     num_rounds(ctx)); +		len -= n; + +		scatterwalk_unmap(p); +		scatterwalk_advance(&walk, n); +		scatterwalk_done(&walk, 0, len); +	} while (len); +} + +static int ccm_encrypt(struct aead_request *req) +{ +	struct crypto_aead *aead = crypto_aead_reqtfm(req); +	struct crypto_aes_ctx *ctx = crypto_aead_ctx(aead); +	struct blkcipher_desc desc = { .info = req->iv }; +	struct blkcipher_walk walk; +	u8 __aligned(8) mac[AES_BLOCK_SIZE]; +	u8 buf[AES_BLOCK_SIZE]; +	u32 len = req->cryptlen; +	int err; + +	err = ccm_init_mac(req, mac, len); +	if (err) +		return err; + +	kernel_neon_begin_partial(6); + +	if (req->assoclen) +		ccm_calculate_auth_mac(req, mac); + +	/* preserve the original iv for the final round */ +	memcpy(buf, req->iv, AES_BLOCK_SIZE); + +	blkcipher_walk_init(&walk, req->dst, req->src, len); +	err = blkcipher_aead_walk_virt_block(&desc, &walk, aead, +					     AES_BLOCK_SIZE); + +	while (walk.nbytes) { +		u32 tail = walk.nbytes % AES_BLOCK_SIZE; + +		if (walk.nbytes == len) +			tail = 0; + +		ce_aes_ccm_encrypt(walk.dst.virt.addr, walk.src.virt.addr, +				   walk.nbytes - tail, ctx->key_enc, +				   num_rounds(ctx), mac, walk.iv); + +		len -= walk.nbytes - tail; +		err = blkcipher_walk_done(&desc, &walk, tail); +	} +	if (!err) +		ce_aes_ccm_final(mac, buf, ctx->key_enc, num_rounds(ctx)); + +	kernel_neon_end(); + +	if (err) +		return err; + +	/* copy authtag to end of dst */ +	scatterwalk_map_and_copy(mac, req->dst, req->cryptlen, +				 crypto_aead_authsize(aead), 1); + +	return 0; +} + +static int ccm_decrypt(struct aead_request *req) +{ +	struct crypto_aead *aead = crypto_aead_reqtfm(req); +	struct crypto_aes_ctx *ctx = crypto_aead_ctx(aead); +	unsigned int authsize = crypto_aead_authsize(aead); +	struct blkcipher_desc desc = { .info = req->iv }; +	struct blkcipher_walk walk; +	u8 __aligned(8) mac[AES_BLOCK_SIZE]; +	u8 buf[AES_BLOCK_SIZE]; +	u32 len = req->cryptlen - authsize; +	int err; + +	err = ccm_init_mac(req, mac, len); +	if (err) +		return err; + +	kernel_neon_begin_partial(6); + +	if (req->assoclen) +		ccm_calculate_auth_mac(req, mac); + +	/* preserve the original iv for the final round */ +	memcpy(buf, req->iv, AES_BLOCK_SIZE); + +	blkcipher_walk_init(&walk, req->dst, req->src, len); +	err = blkcipher_aead_walk_virt_block(&desc, &walk, aead, +					     AES_BLOCK_SIZE); + +	while (walk.nbytes) { +		u32 tail = walk.nbytes % AES_BLOCK_SIZE; + +		if (walk.nbytes == len) +			tail = 0; + +		ce_aes_ccm_decrypt(walk.dst.virt.addr, walk.src.virt.addr, +				   walk.nbytes - tail, ctx->key_enc, +				   num_rounds(ctx), mac, walk.iv); + +		len -= walk.nbytes - tail; +		err = blkcipher_walk_done(&desc, &walk, tail); +	} +	if (!err) +		ce_aes_ccm_final(mac, buf, ctx->key_enc, num_rounds(ctx)); + +	kernel_neon_end(); + +	if (err) +		return err; + +	/* compare calculated auth tag with the stored one */ +	scatterwalk_map_and_copy(buf, req->src, req->cryptlen - authsize, +				 authsize, 0); + +	if (memcmp(mac, buf, authsize)) +		return -EBADMSG; +	return 0; +} + +static struct crypto_alg ccm_aes_alg = { +	.cra_name		= "ccm(aes)", +	.cra_driver_name	= "ccm-aes-ce", +	.cra_priority		= 300, +	.cra_flags		= CRYPTO_ALG_TYPE_AEAD, +	.cra_blocksize		= 1, +	.cra_ctxsize		= sizeof(struct crypto_aes_ctx), +	.cra_alignmask		= 7, +	.cra_type		= &crypto_aead_type, +	.cra_module		= THIS_MODULE, +	.cra_aead = { +		.ivsize		= AES_BLOCK_SIZE, +		.maxauthsize	= AES_BLOCK_SIZE, +		.setkey		= ccm_setkey, +		.setauthsize	= ccm_setauthsize, +		.encrypt	= ccm_encrypt, +		.decrypt	= ccm_decrypt, +	} +}; + +static int __init aes_mod_init(void) +{ +	if (!(elf_hwcap & HWCAP_AES)) +		return -ENODEV; +	return crypto_register_alg(&ccm_aes_alg); +} + +static void __exit aes_mod_exit(void) +{ +	crypto_unregister_alg(&ccm_aes_alg); +} + +module_init(aes_mod_init); +module_exit(aes_mod_exit); + +MODULE_DESCRIPTION("Synchronous AES in CCM mode using ARMv8 Crypto Extensions"); +MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("ccm(aes)"); diff --git a/arch/arm64/crypto/aes-ce-cipher.c b/arch/arm64/crypto/aes-ce-cipher.c new file mode 100644 index 00000000000..2075e1acae6 --- /dev/null +++ b/arch/arm64/crypto/aes-ce-cipher.c @@ -0,0 +1,155 @@ +/* + * aes-ce-cipher.c - core AES cipher using ARMv8 Crypto Extensions + * + * Copyright (C) 2013 - 2014 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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 <asm/neon.h> +#include <crypto/aes.h> +#include <linux/cpufeature.h> +#include <linux/crypto.h> +#include <linux/module.h> + +MODULE_DESCRIPTION("Synchronous AES cipher using ARMv8 Crypto Extensions"); +MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>"); +MODULE_LICENSE("GPL v2"); + +struct aes_block { +	u8 b[AES_BLOCK_SIZE]; +}; + +static int num_rounds(struct crypto_aes_ctx *ctx) +{ +	/* +	 * # of rounds specified by AES: +	 * 128 bit key		10 rounds +	 * 192 bit key		12 rounds +	 * 256 bit key		14 rounds +	 * => n byte key	=> 6 + (n/4) rounds +	 */ +	return 6 + ctx->key_length / 4; +} + +static void aes_cipher_encrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[]) +{ +	struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm); +	struct aes_block *out = (struct aes_block *)dst; +	struct aes_block const *in = (struct aes_block *)src; +	void *dummy0; +	int dummy1; + +	kernel_neon_begin_partial(4); + +	__asm__("	ld1	{v0.16b}, %[in]			;" +		"	ld1	{v1.2d}, [%[key]], #16		;" +		"	cmp	%w[rounds], #10			;" +		"	bmi	0f				;" +		"	bne	3f				;" +		"	mov	v3.16b, v1.16b			;" +		"	b	2f				;" +		"0:	mov	v2.16b, v1.16b			;" +		"	ld1	{v3.2d}, [%[key]], #16		;" +		"1:	aese	v0.16b, v2.16b			;" +		"	aesmc	v0.16b, v0.16b			;" +		"2:	ld1	{v1.2d}, [%[key]], #16		;" +		"	aese	v0.16b, v3.16b			;" +		"	aesmc	v0.16b, v0.16b			;" +		"3:	ld1	{v2.2d}, [%[key]], #16		;" +		"	subs	%w[rounds], %w[rounds], #3	;" +		"	aese	v0.16b, v1.16b			;" +		"	aesmc	v0.16b, v0.16b			;" +		"	ld1	{v3.2d}, [%[key]], #16		;" +		"	bpl	1b				;" +		"	aese	v0.16b, v2.16b			;" +		"	eor	v0.16b, v0.16b, v3.16b		;" +		"	st1	{v0.16b}, %[out]		;" + +	:	[out]		"=Q"(*out), +		[key]		"=r"(dummy0), +		[rounds]	"=r"(dummy1) +	:	[in]		"Q"(*in), +				"1"(ctx->key_enc), +				"2"(num_rounds(ctx) - 2) +	:	"cc"); + +	kernel_neon_end(); +} + +static void aes_cipher_decrypt(struct crypto_tfm *tfm, u8 dst[], u8 const src[]) +{ +	struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm); +	struct aes_block *out = (struct aes_block *)dst; +	struct aes_block const *in = (struct aes_block *)src; +	void *dummy0; +	int dummy1; + +	kernel_neon_begin_partial(4); + +	__asm__("	ld1	{v0.16b}, %[in]			;" +		"	ld1	{v1.2d}, [%[key]], #16		;" +		"	cmp	%w[rounds], #10			;" +		"	bmi	0f				;" +		"	bne	3f				;" +		"	mov	v3.16b, v1.16b			;" +		"	b	2f				;" +		"0:	mov	v2.16b, v1.16b			;" +		"	ld1	{v3.2d}, [%[key]], #16		;" +		"1:	aesd	v0.16b, v2.16b			;" +		"	aesimc	v0.16b, v0.16b			;" +		"2:	ld1	{v1.2d}, [%[key]], #16		;" +		"	aesd	v0.16b, v3.16b			;" +		"	aesimc	v0.16b, v0.16b			;" +		"3:	ld1	{v2.2d}, [%[key]], #16		;" +		"	subs	%w[rounds], %w[rounds], #3	;" +		"	aesd	v0.16b, v1.16b			;" +		"	aesimc	v0.16b, v0.16b			;" +		"	ld1	{v3.2d}, [%[key]], #16		;" +		"	bpl	1b				;" +		"	aesd	v0.16b, v2.16b			;" +		"	eor	v0.16b, v0.16b, v3.16b		;" +		"	st1	{v0.16b}, %[out]		;" + +	:	[out]		"=Q"(*out), +		[key]		"=r"(dummy0), +		[rounds]	"=r"(dummy1) +	:	[in]		"Q"(*in), +				"1"(ctx->key_dec), +				"2"(num_rounds(ctx) - 2) +	:	"cc"); + +	kernel_neon_end(); +} + +static struct crypto_alg aes_alg = { +	.cra_name		= "aes", +	.cra_driver_name	= "aes-ce", +	.cra_priority		= 300, +	.cra_flags		= CRYPTO_ALG_TYPE_CIPHER, +	.cra_blocksize		= AES_BLOCK_SIZE, +	.cra_ctxsize		= sizeof(struct crypto_aes_ctx), +	.cra_module		= THIS_MODULE, +	.cra_cipher = { +		.cia_min_keysize	= AES_MIN_KEY_SIZE, +		.cia_max_keysize	= AES_MAX_KEY_SIZE, +		.cia_setkey		= crypto_aes_set_key, +		.cia_encrypt		= aes_cipher_encrypt, +		.cia_decrypt		= aes_cipher_decrypt +	} +}; + +static int __init aes_mod_init(void) +{ +	return crypto_register_alg(&aes_alg); +} + +static void __exit aes_mod_exit(void) +{ +	crypto_unregister_alg(&aes_alg); +} + +module_cpu_feature_match(AES, aes_mod_init); +module_exit(aes_mod_exit); diff --git a/arch/arm64/crypto/aes-ce.S b/arch/arm64/crypto/aes-ce.S new file mode 100644 index 00000000000..685a18f731e --- /dev/null +++ b/arch/arm64/crypto/aes-ce.S @@ -0,0 +1,133 @@ +/* + * linux/arch/arm64/crypto/aes-ce.S - AES cipher for ARMv8 with + *                                    Crypto Extensions + * + * Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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/linkage.h> + +#define AES_ENTRY(func)		ENTRY(ce_ ## func) +#define AES_ENDPROC(func)	ENDPROC(ce_ ## func) + +	.arch		armv8-a+crypto + +	/* preload all round keys */ +	.macro		load_round_keys, rounds, rk +	cmp		\rounds, #12 +	blo		2222f		/* 128 bits */ +	beq		1111f		/* 192 bits */ +	ld1		{v17.16b-v18.16b}, [\rk], #32 +1111:	ld1		{v19.16b-v20.16b}, [\rk], #32 +2222:	ld1		{v21.16b-v24.16b}, [\rk], #64 +	ld1		{v25.16b-v28.16b}, [\rk], #64 +	ld1		{v29.16b-v31.16b}, [\rk] +	.endm + +	/* prepare for encryption with key in rk[] */ +	.macro		enc_prepare, rounds, rk, ignore +	load_round_keys	\rounds, \rk +	.endm + +	/* prepare for encryption (again) but with new key in rk[] */ +	.macro		enc_switch_key, rounds, rk, ignore +	load_round_keys	\rounds, \rk +	.endm + +	/* prepare for decryption with key in rk[] */ +	.macro		dec_prepare, rounds, rk, ignore +	load_round_keys	\rounds, \rk +	.endm + +	.macro		do_enc_Nx, de, mc, k, i0, i1, i2, i3 +	aes\de		\i0\().16b, \k\().16b +	.ifnb		\i1 +	aes\de		\i1\().16b, \k\().16b +	.ifnb		\i3 +	aes\de		\i2\().16b, \k\().16b +	aes\de		\i3\().16b, \k\().16b +	.endif +	.endif +	aes\mc		\i0\().16b, \i0\().16b +	.ifnb		\i1 +	aes\mc		\i1\().16b, \i1\().16b +	.ifnb		\i3 +	aes\mc		\i2\().16b, \i2\().16b +	aes\mc		\i3\().16b, \i3\().16b +	.endif +	.endif +	.endm + +	/* up to 4 interleaved encryption rounds with the same round key */ +	.macro		round_Nx, enc, k, i0, i1, i2, i3 +	.ifc		\enc, e +	do_enc_Nx	e, mc, \k, \i0, \i1, \i2, \i3 +	.else +	do_enc_Nx	d, imc, \k, \i0, \i1, \i2, \i3 +	.endif +	.endm + +	/* up to 4 interleaved final rounds */ +	.macro		fin_round_Nx, de, k, k2, i0, i1, i2, i3 +	aes\de		\i0\().16b, \k\().16b +	.ifnb		\i1 +	aes\de		\i1\().16b, \k\().16b +	.ifnb		\i3 +	aes\de		\i2\().16b, \k\().16b +	aes\de		\i3\().16b, \k\().16b +	.endif +	.endif +	eor		\i0\().16b, \i0\().16b, \k2\().16b +	.ifnb		\i1 +	eor		\i1\().16b, \i1\().16b, \k2\().16b +	.ifnb		\i3 +	eor		\i2\().16b, \i2\().16b, \k2\().16b +	eor		\i3\().16b, \i3\().16b, \k2\().16b +	.endif +	.endif +	.endm + +	/* up to 4 interleaved blocks */ +	.macro		do_block_Nx, enc, rounds, i0, i1, i2, i3 +	cmp		\rounds, #12 +	blo		2222f		/* 128 bits */ +	beq		1111f		/* 192 bits */ +	round_Nx	\enc, v17, \i0, \i1, \i2, \i3 +	round_Nx	\enc, v18, \i0, \i1, \i2, \i3 +1111:	round_Nx	\enc, v19, \i0, \i1, \i2, \i3 +	round_Nx	\enc, v20, \i0, \i1, \i2, \i3 +2222:	.irp		key, v21, v22, v23, v24, v25, v26, v27, v28, v29 +	round_Nx	\enc, \key, \i0, \i1, \i2, \i3 +	.endr +	fin_round_Nx	\enc, v30, v31, \i0, \i1, \i2, \i3 +	.endm + +	.macro		encrypt_block, in, rounds, t0, t1, t2 +	do_block_Nx	e, \rounds, \in +	.endm + +	.macro		encrypt_block2x, i0, i1, rounds, t0, t1, t2 +	do_block_Nx	e, \rounds, \i0, \i1 +	.endm + +	.macro		encrypt_block4x, i0, i1, i2, i3, rounds, t0, t1, t2 +	do_block_Nx	e, \rounds, \i0, \i1, \i2, \i3 +	.endm + +	.macro		decrypt_block, in, rounds, t0, t1, t2 +	do_block_Nx	d, \rounds, \in +	.endm + +	.macro		decrypt_block2x, i0, i1, rounds, t0, t1, t2 +	do_block_Nx	d, \rounds, \i0, \i1 +	.endm + +	.macro		decrypt_block4x, i0, i1, i2, i3, rounds, t0, t1, t2 +	do_block_Nx	d, \rounds, \i0, \i1, \i2, \i3 +	.endm + +#include "aes-modes.S" diff --git a/arch/arm64/crypto/aes-glue.c b/arch/arm64/crypto/aes-glue.c new file mode 100644 index 00000000000..79cd911ef88 --- /dev/null +++ b/arch/arm64/crypto/aes-glue.c @@ -0,0 +1,446 @@ +/* + * linux/arch/arm64/crypto/aes-glue.c - wrapper code for ARMv8 AES + * + * Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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 <asm/neon.h> +#include <asm/hwcap.h> +#include <crypto/aes.h> +#include <crypto/ablk_helper.h> +#include <crypto/algapi.h> +#include <linux/module.h> +#include <linux/cpufeature.h> + +#ifdef USE_V8_CRYPTO_EXTENSIONS +#define MODE			"ce" +#define PRIO			300 +#define aes_ecb_encrypt		ce_aes_ecb_encrypt +#define aes_ecb_decrypt		ce_aes_ecb_decrypt +#define aes_cbc_encrypt		ce_aes_cbc_encrypt +#define aes_cbc_decrypt		ce_aes_cbc_decrypt +#define aes_ctr_encrypt		ce_aes_ctr_encrypt +#define aes_xts_encrypt		ce_aes_xts_encrypt +#define aes_xts_decrypt		ce_aes_xts_decrypt +MODULE_DESCRIPTION("AES-ECB/CBC/CTR/XTS using ARMv8 Crypto Extensions"); +#else +#define MODE			"neon" +#define PRIO			200 +#define aes_ecb_encrypt		neon_aes_ecb_encrypt +#define aes_ecb_decrypt		neon_aes_ecb_decrypt +#define aes_cbc_encrypt		neon_aes_cbc_encrypt +#define aes_cbc_decrypt		neon_aes_cbc_decrypt +#define aes_ctr_encrypt		neon_aes_ctr_encrypt +#define aes_xts_encrypt		neon_aes_xts_encrypt +#define aes_xts_decrypt		neon_aes_xts_decrypt +MODULE_DESCRIPTION("AES-ECB/CBC/CTR/XTS using ARMv8 NEON"); +MODULE_ALIAS("ecb(aes)"); +MODULE_ALIAS("cbc(aes)"); +MODULE_ALIAS("ctr(aes)"); +MODULE_ALIAS("xts(aes)"); +#endif + +MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>"); +MODULE_LICENSE("GPL v2"); + +/* defined in aes-modes.S */ +asmlinkage void aes_ecb_encrypt(u8 out[], u8 const in[], u8 const rk[], +				int rounds, int blocks, int first); +asmlinkage void aes_ecb_decrypt(u8 out[], u8 const in[], u8 const rk[], +				int rounds, int blocks, int first); + +asmlinkage void aes_cbc_encrypt(u8 out[], u8 const in[], u8 const rk[], +				int rounds, int blocks, u8 iv[], int first); +asmlinkage void aes_cbc_decrypt(u8 out[], u8 const in[], u8 const rk[], +				int rounds, int blocks, u8 iv[], int first); + +asmlinkage void aes_ctr_encrypt(u8 out[], u8 const in[], u8 const rk[], +				int rounds, int blocks, u8 ctr[], int first); + +asmlinkage void aes_xts_encrypt(u8 out[], u8 const in[], u8 const rk1[], +				int rounds, int blocks, u8 const rk2[], u8 iv[], +				int first); +asmlinkage void aes_xts_decrypt(u8 out[], u8 const in[], u8 const rk1[], +				int rounds, int blocks, u8 const rk2[], u8 iv[], +				int first); + +struct crypto_aes_xts_ctx { +	struct crypto_aes_ctx key1; +	struct crypto_aes_ctx __aligned(8) key2; +}; + +static int xts_set_key(struct crypto_tfm *tfm, const u8 *in_key, +		       unsigned int key_len) +{ +	struct crypto_aes_xts_ctx *ctx = crypto_tfm_ctx(tfm); +	int ret; + +	ret = crypto_aes_expand_key(&ctx->key1, in_key, key_len / 2); +	if (!ret) +		ret = crypto_aes_expand_key(&ctx->key2, &in_key[key_len / 2], +					    key_len / 2); +	if (!ret) +		return 0; + +	tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; +	return -EINVAL; +} + +static int ecb_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, +		       struct scatterlist *src, unsigned int nbytes) +{ +	struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); +	int err, first, rounds = 6 + ctx->key_length / 4; +	struct blkcipher_walk walk; +	unsigned int blocks; + +	desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; +	blkcipher_walk_init(&walk, dst, src, nbytes); +	err = blkcipher_walk_virt(desc, &walk); + +	kernel_neon_begin(); +	for (first = 1; (blocks = (walk.nbytes / AES_BLOCK_SIZE)); first = 0) { +		aes_ecb_encrypt(walk.dst.virt.addr, walk.src.virt.addr, +				(u8 *)ctx->key_enc, rounds, blocks, first); +		err = blkcipher_walk_done(desc, &walk, walk.nbytes % AES_BLOCK_SIZE); +	} +	kernel_neon_end(); +	return err; +} + +static int ecb_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, +		       struct scatterlist *src, unsigned int nbytes) +{ +	struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); +	int err, first, rounds = 6 + ctx->key_length / 4; +	struct blkcipher_walk walk; +	unsigned int blocks; + +	desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; +	blkcipher_walk_init(&walk, dst, src, nbytes); +	err = blkcipher_walk_virt(desc, &walk); + +	kernel_neon_begin(); +	for (first = 1; (blocks = (walk.nbytes / AES_BLOCK_SIZE)); first = 0) { +		aes_ecb_decrypt(walk.dst.virt.addr, walk.src.virt.addr, +				(u8 *)ctx->key_dec, rounds, blocks, first); +		err = blkcipher_walk_done(desc, &walk, walk.nbytes % AES_BLOCK_SIZE); +	} +	kernel_neon_end(); +	return err; +} + +static int cbc_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, +		       struct scatterlist *src, unsigned int nbytes) +{ +	struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); +	int err, first, rounds = 6 + ctx->key_length / 4; +	struct blkcipher_walk walk; +	unsigned int blocks; + +	desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; +	blkcipher_walk_init(&walk, dst, src, nbytes); +	err = blkcipher_walk_virt(desc, &walk); + +	kernel_neon_begin(); +	for (first = 1; (blocks = (walk.nbytes / AES_BLOCK_SIZE)); first = 0) { +		aes_cbc_encrypt(walk.dst.virt.addr, walk.src.virt.addr, +				(u8 *)ctx->key_enc, rounds, blocks, walk.iv, +				first); +		err = blkcipher_walk_done(desc, &walk, walk.nbytes % AES_BLOCK_SIZE); +	} +	kernel_neon_end(); +	return err; +} + +static int cbc_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, +		       struct scatterlist *src, unsigned int nbytes) +{ +	struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); +	int err, first, rounds = 6 + ctx->key_length / 4; +	struct blkcipher_walk walk; +	unsigned int blocks; + +	desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; +	blkcipher_walk_init(&walk, dst, src, nbytes); +	err = blkcipher_walk_virt(desc, &walk); + +	kernel_neon_begin(); +	for (first = 1; (blocks = (walk.nbytes / AES_BLOCK_SIZE)); first = 0) { +		aes_cbc_decrypt(walk.dst.virt.addr, walk.src.virt.addr, +				(u8 *)ctx->key_dec, rounds, blocks, walk.iv, +				first); +		err = blkcipher_walk_done(desc, &walk, walk.nbytes % AES_BLOCK_SIZE); +	} +	kernel_neon_end(); +	return err; +} + +static int ctr_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, +		       struct scatterlist *src, unsigned int nbytes) +{ +	struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); +	int err, first, rounds = 6 + ctx->key_length / 4; +	struct blkcipher_walk walk; +	int blocks; + +	desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; +	blkcipher_walk_init(&walk, dst, src, nbytes); +	err = blkcipher_walk_virt_block(desc, &walk, AES_BLOCK_SIZE); + +	first = 1; +	kernel_neon_begin(); +	while ((blocks = (walk.nbytes / AES_BLOCK_SIZE))) { +		aes_ctr_encrypt(walk.dst.virt.addr, walk.src.virt.addr, +				(u8 *)ctx->key_enc, rounds, blocks, walk.iv, +				first); +		first = 0; +		nbytes -= blocks * AES_BLOCK_SIZE; +		if (nbytes && nbytes == walk.nbytes % AES_BLOCK_SIZE) +			break; +		err = blkcipher_walk_done(desc, &walk, +					  walk.nbytes % AES_BLOCK_SIZE); +	} +	if (nbytes) { +		u8 *tdst = walk.dst.virt.addr + blocks * AES_BLOCK_SIZE; +		u8 *tsrc = walk.src.virt.addr + blocks * AES_BLOCK_SIZE; +		u8 __aligned(8) tail[AES_BLOCK_SIZE]; + +		/* +		 * Minimum alignment is 8 bytes, so if nbytes is <= 8, we need +		 * to tell aes_ctr_encrypt() to only read half a block. +		 */ +		blocks = (nbytes <= 8) ? -1 : 1; + +		aes_ctr_encrypt(tail, tsrc, (u8 *)ctx->key_enc, rounds, +				blocks, walk.iv, first); +		memcpy(tdst, tail, nbytes); +		err = blkcipher_walk_done(desc, &walk, 0); +	} +	kernel_neon_end(); + +	return err; +} + +static int xts_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, +		       struct scatterlist *src, unsigned int nbytes) +{ +	struct crypto_aes_xts_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); +	int err, first, rounds = 6 + ctx->key1.key_length / 4; +	struct blkcipher_walk walk; +	unsigned int blocks; + +	desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; +	blkcipher_walk_init(&walk, dst, src, nbytes); +	err = blkcipher_walk_virt(desc, &walk); + +	kernel_neon_begin(); +	for (first = 1; (blocks = (walk.nbytes / AES_BLOCK_SIZE)); first = 0) { +		aes_xts_encrypt(walk.dst.virt.addr, walk.src.virt.addr, +				(u8 *)ctx->key1.key_enc, rounds, blocks, +				(u8 *)ctx->key2.key_enc, walk.iv, first); +		err = blkcipher_walk_done(desc, &walk, walk.nbytes % AES_BLOCK_SIZE); +	} +	kernel_neon_end(); + +	return err; +} + +static int xts_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, +		       struct scatterlist *src, unsigned int nbytes) +{ +	struct crypto_aes_xts_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); +	int err, first, rounds = 6 + ctx->key1.key_length / 4; +	struct blkcipher_walk walk; +	unsigned int blocks; + +	desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; +	blkcipher_walk_init(&walk, dst, src, nbytes); +	err = blkcipher_walk_virt(desc, &walk); + +	kernel_neon_begin(); +	for (first = 1; (blocks = (walk.nbytes / AES_BLOCK_SIZE)); first = 0) { +		aes_xts_decrypt(walk.dst.virt.addr, walk.src.virt.addr, +				(u8 *)ctx->key1.key_dec, rounds, blocks, +				(u8 *)ctx->key2.key_enc, walk.iv, first); +		err = blkcipher_walk_done(desc, &walk, walk.nbytes % AES_BLOCK_SIZE); +	} +	kernel_neon_end(); + +	return err; +} + +static struct crypto_alg aes_algs[] = { { +	.cra_name		= "__ecb-aes-" MODE, +	.cra_driver_name	= "__driver-ecb-aes-" MODE, +	.cra_priority		= 0, +	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER, +	.cra_blocksize		= AES_BLOCK_SIZE, +	.cra_ctxsize		= sizeof(struct crypto_aes_ctx), +	.cra_alignmask		= 7, +	.cra_type		= &crypto_blkcipher_type, +	.cra_module		= THIS_MODULE, +	.cra_blkcipher = { +		.min_keysize	= AES_MIN_KEY_SIZE, +		.max_keysize	= AES_MAX_KEY_SIZE, +		.ivsize		= AES_BLOCK_SIZE, +		.setkey		= crypto_aes_set_key, +		.encrypt	= ecb_encrypt, +		.decrypt	= ecb_decrypt, +	}, +}, { +	.cra_name		= "__cbc-aes-" MODE, +	.cra_driver_name	= "__driver-cbc-aes-" MODE, +	.cra_priority		= 0, +	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER, +	.cra_blocksize		= AES_BLOCK_SIZE, +	.cra_ctxsize		= sizeof(struct crypto_aes_ctx), +	.cra_alignmask		= 7, +	.cra_type		= &crypto_blkcipher_type, +	.cra_module		= THIS_MODULE, +	.cra_blkcipher = { +		.min_keysize	= AES_MIN_KEY_SIZE, +		.max_keysize	= AES_MAX_KEY_SIZE, +		.ivsize		= AES_BLOCK_SIZE, +		.setkey		= crypto_aes_set_key, +		.encrypt	= cbc_encrypt, +		.decrypt	= cbc_decrypt, +	}, +}, { +	.cra_name		= "__ctr-aes-" MODE, +	.cra_driver_name	= "__driver-ctr-aes-" MODE, +	.cra_priority		= 0, +	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER, +	.cra_blocksize		= 1, +	.cra_ctxsize		= sizeof(struct crypto_aes_ctx), +	.cra_alignmask		= 7, +	.cra_type		= &crypto_blkcipher_type, +	.cra_module		= THIS_MODULE, +	.cra_blkcipher = { +		.min_keysize	= AES_MIN_KEY_SIZE, +		.max_keysize	= AES_MAX_KEY_SIZE, +		.ivsize		= AES_BLOCK_SIZE, +		.setkey		= crypto_aes_set_key, +		.encrypt	= ctr_encrypt, +		.decrypt	= ctr_encrypt, +	}, +}, { +	.cra_name		= "__xts-aes-" MODE, +	.cra_driver_name	= "__driver-xts-aes-" MODE, +	.cra_priority		= 0, +	.cra_flags		= CRYPTO_ALG_TYPE_BLKCIPHER, +	.cra_blocksize		= AES_BLOCK_SIZE, +	.cra_ctxsize		= sizeof(struct crypto_aes_xts_ctx), +	.cra_alignmask		= 7, +	.cra_type		= &crypto_blkcipher_type, +	.cra_module		= THIS_MODULE, +	.cra_blkcipher = { +		.min_keysize	= 2 * AES_MIN_KEY_SIZE, +		.max_keysize	= 2 * AES_MAX_KEY_SIZE, +		.ivsize		= AES_BLOCK_SIZE, +		.setkey		= xts_set_key, +		.encrypt	= xts_encrypt, +		.decrypt	= xts_decrypt, +	}, +}, { +	.cra_name		= "ecb(aes)", +	.cra_driver_name	= "ecb-aes-" MODE, +	.cra_priority		= PRIO, +	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, +	.cra_blocksize		= AES_BLOCK_SIZE, +	.cra_ctxsize		= sizeof(struct async_helper_ctx), +	.cra_alignmask		= 7, +	.cra_type		= &crypto_ablkcipher_type, +	.cra_module		= THIS_MODULE, +	.cra_init		= ablk_init, +	.cra_exit		= ablk_exit, +	.cra_ablkcipher = { +		.min_keysize	= AES_MIN_KEY_SIZE, +		.max_keysize	= AES_MAX_KEY_SIZE, +		.ivsize		= AES_BLOCK_SIZE, +		.setkey		= ablk_set_key, +		.encrypt	= ablk_encrypt, +		.decrypt	= ablk_decrypt, +	} +}, { +	.cra_name		= "cbc(aes)", +	.cra_driver_name	= "cbc-aes-" MODE, +	.cra_priority		= PRIO, +	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, +	.cra_blocksize		= AES_BLOCK_SIZE, +	.cra_ctxsize		= sizeof(struct async_helper_ctx), +	.cra_alignmask		= 7, +	.cra_type		= &crypto_ablkcipher_type, +	.cra_module		= THIS_MODULE, +	.cra_init		= ablk_init, +	.cra_exit		= ablk_exit, +	.cra_ablkcipher = { +		.min_keysize	= AES_MIN_KEY_SIZE, +		.max_keysize	= AES_MAX_KEY_SIZE, +		.ivsize		= AES_BLOCK_SIZE, +		.setkey		= ablk_set_key, +		.encrypt	= ablk_encrypt, +		.decrypt	= ablk_decrypt, +	} +}, { +	.cra_name		= "ctr(aes)", +	.cra_driver_name	= "ctr-aes-" MODE, +	.cra_priority		= PRIO, +	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, +	.cra_blocksize		= 1, +	.cra_ctxsize		= sizeof(struct async_helper_ctx), +	.cra_alignmask		= 7, +	.cra_type		= &crypto_ablkcipher_type, +	.cra_module		= THIS_MODULE, +	.cra_init		= ablk_init, +	.cra_exit		= ablk_exit, +	.cra_ablkcipher = { +		.min_keysize	= AES_MIN_KEY_SIZE, +		.max_keysize	= AES_MAX_KEY_SIZE, +		.ivsize		= AES_BLOCK_SIZE, +		.setkey		= ablk_set_key, +		.encrypt	= ablk_encrypt, +		.decrypt	= ablk_decrypt, +	} +}, { +	.cra_name		= "xts(aes)", +	.cra_driver_name	= "xts-aes-" MODE, +	.cra_priority		= PRIO, +	.cra_flags		= CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, +	.cra_blocksize		= AES_BLOCK_SIZE, +	.cra_ctxsize		= sizeof(struct async_helper_ctx), +	.cra_alignmask		= 7, +	.cra_type		= &crypto_ablkcipher_type, +	.cra_module		= THIS_MODULE, +	.cra_init		= ablk_init, +	.cra_exit		= ablk_exit, +	.cra_ablkcipher = { +		.min_keysize	= 2 * AES_MIN_KEY_SIZE, +		.max_keysize	= 2 * AES_MAX_KEY_SIZE, +		.ivsize		= AES_BLOCK_SIZE, +		.setkey		= ablk_set_key, +		.encrypt	= ablk_encrypt, +		.decrypt	= ablk_decrypt, +	} +} }; + +static int __init aes_init(void) +{ +	return crypto_register_algs(aes_algs, ARRAY_SIZE(aes_algs)); +} + +static void __exit aes_exit(void) +{ +	crypto_unregister_algs(aes_algs, ARRAY_SIZE(aes_algs)); +} + +#ifdef USE_V8_CRYPTO_EXTENSIONS +module_cpu_feature_match(AES, aes_init); +#else +module_init(aes_init); +#endif +module_exit(aes_exit); diff --git a/arch/arm64/crypto/aes-modes.S b/arch/arm64/crypto/aes-modes.S new file mode 100644 index 00000000000..f6e372c528e --- /dev/null +++ b/arch/arm64/crypto/aes-modes.S @@ -0,0 +1,532 @@ +/* + * linux/arch/arm64/crypto/aes-modes.S - chaining mode wrappers for AES + * + * Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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. + */ + +/* included by aes-ce.S and aes-neon.S */ + +	.text +	.align		4 + +/* + * There are several ways to instantiate this code: + * - no interleave, all inline + * - 2-way interleave, 2x calls out of line (-DINTERLEAVE=2) + * - 2-way interleave, all inline (-DINTERLEAVE=2 -DINTERLEAVE_INLINE) + * - 4-way interleave, 4x calls out of line (-DINTERLEAVE=4) + * - 4-way interleave, all inline (-DINTERLEAVE=4 -DINTERLEAVE_INLINE) + * + * Macros imported by this code: + * - enc_prepare	- setup NEON registers for encryption + * - dec_prepare	- setup NEON registers for decryption + * - enc_switch_key	- change to new key after having prepared for encryption + * - encrypt_block	- encrypt a single block + * - decrypt block	- decrypt a single block + * - encrypt_block2x	- encrypt 2 blocks in parallel (if INTERLEAVE == 2) + * - decrypt_block2x	- decrypt 2 blocks in parallel (if INTERLEAVE == 2) + * - encrypt_block4x	- encrypt 4 blocks in parallel (if INTERLEAVE == 4) + * - decrypt_block4x	- decrypt 4 blocks in parallel (if INTERLEAVE == 4) + */ + +#if defined(INTERLEAVE) && !defined(INTERLEAVE_INLINE) +#define FRAME_PUSH	stp x29, x30, [sp,#-16]! ; mov x29, sp +#define FRAME_POP	ldp x29, x30, [sp],#16 + +#if INTERLEAVE == 2 + +aes_encrypt_block2x: +	encrypt_block2x	v0, v1, w3, x2, x6, w7 +	ret +ENDPROC(aes_encrypt_block2x) + +aes_decrypt_block2x: +	decrypt_block2x	v0, v1, w3, x2, x6, w7 +	ret +ENDPROC(aes_decrypt_block2x) + +#elif INTERLEAVE == 4 + +aes_encrypt_block4x: +	encrypt_block4x	v0, v1, v2, v3, w3, x2, x6, w7 +	ret +ENDPROC(aes_encrypt_block4x) + +aes_decrypt_block4x: +	decrypt_block4x	v0, v1, v2, v3, w3, x2, x6, w7 +	ret +ENDPROC(aes_decrypt_block4x) + +#else +#error INTERLEAVE should equal 2 or 4 +#endif + +	.macro		do_encrypt_block2x +	bl		aes_encrypt_block2x +	.endm + +	.macro		do_decrypt_block2x +	bl		aes_decrypt_block2x +	.endm + +	.macro		do_encrypt_block4x +	bl		aes_encrypt_block4x +	.endm + +	.macro		do_decrypt_block4x +	bl		aes_decrypt_block4x +	.endm + +#else +#define FRAME_PUSH +#define FRAME_POP + +	.macro		do_encrypt_block2x +	encrypt_block2x	v0, v1, w3, x2, x6, w7 +	.endm + +	.macro		do_decrypt_block2x +	decrypt_block2x	v0, v1, w3, x2, x6, w7 +	.endm + +	.macro		do_encrypt_block4x +	encrypt_block4x	v0, v1, v2, v3, w3, x2, x6, w7 +	.endm + +	.macro		do_decrypt_block4x +	decrypt_block4x	v0, v1, v2, v3, w3, x2, x6, w7 +	.endm + +#endif + +	/* +	 * aes_ecb_encrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, +	 *		   int blocks, int first) +	 * aes_ecb_decrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, +	 *		   int blocks, int first) +	 */ + +AES_ENTRY(aes_ecb_encrypt) +	FRAME_PUSH +	cbz		w5, .LecbencloopNx + +	enc_prepare	w3, x2, x5 + +.LecbencloopNx: +#if INTERLEAVE >= 2 +	subs		w4, w4, #INTERLEAVE +	bmi		.Lecbenc1x +#if INTERLEAVE == 2 +	ld1		{v0.16b-v1.16b}, [x1], #32	/* get 2 pt blocks */ +	do_encrypt_block2x +	st1		{v0.16b-v1.16b}, [x0], #32 +#else +	ld1		{v0.16b-v3.16b}, [x1], #64	/* get 4 pt blocks */ +	do_encrypt_block4x +	st1		{v0.16b-v3.16b}, [x0], #64 +#endif +	b		.LecbencloopNx +.Lecbenc1x: +	adds		w4, w4, #INTERLEAVE +	beq		.Lecbencout +#endif +.Lecbencloop: +	ld1		{v0.16b}, [x1], #16		/* get next pt block */ +	encrypt_block	v0, w3, x2, x5, w6 +	st1		{v0.16b}, [x0], #16 +	subs		w4, w4, #1 +	bne		.Lecbencloop +.Lecbencout: +	FRAME_POP +	ret +AES_ENDPROC(aes_ecb_encrypt) + + +AES_ENTRY(aes_ecb_decrypt) +	FRAME_PUSH +	cbz		w5, .LecbdecloopNx + +	dec_prepare	w3, x2, x5 + +.LecbdecloopNx: +#if INTERLEAVE >= 2 +	subs		w4, w4, #INTERLEAVE +	bmi		.Lecbdec1x +#if INTERLEAVE == 2 +	ld1		{v0.16b-v1.16b}, [x1], #32	/* get 2 ct blocks */ +	do_decrypt_block2x +	st1		{v0.16b-v1.16b}, [x0], #32 +#else +	ld1		{v0.16b-v3.16b}, [x1], #64	/* get 4 ct blocks */ +	do_decrypt_block4x +	st1		{v0.16b-v3.16b}, [x0], #64 +#endif +	b		.LecbdecloopNx +.Lecbdec1x: +	adds		w4, w4, #INTERLEAVE +	beq		.Lecbdecout +#endif +.Lecbdecloop: +	ld1		{v0.16b}, [x1], #16		/* get next ct block */ +	decrypt_block	v0, w3, x2, x5, w6 +	st1		{v0.16b}, [x0], #16 +	subs		w4, w4, #1 +	bne		.Lecbdecloop +.Lecbdecout: +	FRAME_POP +	ret +AES_ENDPROC(aes_ecb_decrypt) + + +	/* +	 * aes_cbc_encrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, +	 *		   int blocks, u8 iv[], int first) +	 * aes_cbc_decrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, +	 *		   int blocks, u8 iv[], int first) +	 */ + +AES_ENTRY(aes_cbc_encrypt) +	cbz		w6, .Lcbcencloop + +	ld1		{v0.16b}, [x5]			/* get iv */ +	enc_prepare	w3, x2, x5 + +.Lcbcencloop: +	ld1		{v1.16b}, [x1], #16		/* get next pt block */ +	eor		v0.16b, v0.16b, v1.16b		/* ..and xor with iv */ +	encrypt_block	v0, w3, x2, x5, w6 +	st1		{v0.16b}, [x0], #16 +	subs		w4, w4, #1 +	bne		.Lcbcencloop +	ret +AES_ENDPROC(aes_cbc_encrypt) + + +AES_ENTRY(aes_cbc_decrypt) +	FRAME_PUSH +	cbz		w6, .LcbcdecloopNx + +	ld1		{v7.16b}, [x5]			/* get iv */ +	dec_prepare	w3, x2, x5 + +.LcbcdecloopNx: +#if INTERLEAVE >= 2 +	subs		w4, w4, #INTERLEAVE +	bmi		.Lcbcdec1x +#if INTERLEAVE == 2 +	ld1		{v0.16b-v1.16b}, [x1], #32	/* get 2 ct blocks */ +	mov		v2.16b, v0.16b +	mov		v3.16b, v1.16b +	do_decrypt_block2x +	eor		v0.16b, v0.16b, v7.16b +	eor		v1.16b, v1.16b, v2.16b +	mov		v7.16b, v3.16b +	st1		{v0.16b-v1.16b}, [x0], #32 +#else +	ld1		{v0.16b-v3.16b}, [x1], #64	/* get 4 ct blocks */ +	mov		v4.16b, v0.16b +	mov		v5.16b, v1.16b +	mov		v6.16b, v2.16b +	do_decrypt_block4x +	sub		x1, x1, #16 +	eor		v0.16b, v0.16b, v7.16b +	eor		v1.16b, v1.16b, v4.16b +	ld1		{v7.16b}, [x1], #16		/* reload 1 ct block */ +	eor		v2.16b, v2.16b, v5.16b +	eor		v3.16b, v3.16b, v6.16b +	st1		{v0.16b-v3.16b}, [x0], #64 +#endif +	b		.LcbcdecloopNx +.Lcbcdec1x: +	adds		w4, w4, #INTERLEAVE +	beq		.Lcbcdecout +#endif +.Lcbcdecloop: +	ld1		{v1.16b}, [x1], #16		/* get next ct block */ +	mov		v0.16b, v1.16b			/* ...and copy to v0 */ +	decrypt_block	v0, w3, x2, x5, w6 +	eor		v0.16b, v0.16b, v7.16b		/* xor with iv => pt */ +	mov		v7.16b, v1.16b			/* ct is next iv */ +	st1		{v0.16b}, [x0], #16 +	subs		w4, w4, #1 +	bne		.Lcbcdecloop +.Lcbcdecout: +	FRAME_POP +	ret +AES_ENDPROC(aes_cbc_decrypt) + + +	/* +	 * aes_ctr_encrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, +	 *		   int blocks, u8 ctr[], int first) +	 */ + +AES_ENTRY(aes_ctr_encrypt) +	FRAME_PUSH +	cbnz		w6, .Lctrfirst		/* 1st time around? */ +	umov		x5, v4.d[1]		/* keep swabbed ctr in reg */ +	rev		x5, x5 +#if INTERLEAVE >= 2 +	cmn		w5, w4			/* 32 bit overflow? */ +	bcs		.Lctrinc +	add		x5, x5, #1		/* increment BE ctr */ +	b		.LctrincNx +#else +	b		.Lctrinc +#endif +.Lctrfirst: +	enc_prepare	w3, x2, x6 +	ld1		{v4.16b}, [x5] +	umov		x5, v4.d[1]		/* keep swabbed ctr in reg */ +	rev		x5, x5 +#if INTERLEAVE >= 2 +	cmn		w5, w4			/* 32 bit overflow? */ +	bcs		.Lctrloop +.LctrloopNx: +	subs		w4, w4, #INTERLEAVE +	bmi		.Lctr1x +#if INTERLEAVE == 2 +	mov		v0.8b, v4.8b +	mov		v1.8b, v4.8b +	rev		x7, x5 +	add		x5, x5, #1 +	ins		v0.d[1], x7 +	rev		x7, x5 +	add		x5, x5, #1 +	ins		v1.d[1], x7 +	ld1		{v2.16b-v3.16b}, [x1], #32	/* get 2 input blocks */ +	do_encrypt_block2x +	eor		v0.16b, v0.16b, v2.16b +	eor		v1.16b, v1.16b, v3.16b +	st1		{v0.16b-v1.16b}, [x0], #32 +#else +	ldr		q8, =0x30000000200000001	/* addends 1,2,3[,0] */ +	dup		v7.4s, w5 +	mov		v0.16b, v4.16b +	add		v7.4s, v7.4s, v8.4s +	mov		v1.16b, v4.16b +	rev32		v8.16b, v7.16b +	mov		v2.16b, v4.16b +	mov		v3.16b, v4.16b +	mov		v1.s[3], v8.s[0] +	mov		v2.s[3], v8.s[1] +	mov		v3.s[3], v8.s[2] +	ld1		{v5.16b-v7.16b}, [x1], #48	/* get 3 input blocks */ +	do_encrypt_block4x +	eor		v0.16b, v5.16b, v0.16b +	ld1		{v5.16b}, [x1], #16		/* get 1 input block  */ +	eor		v1.16b, v6.16b, v1.16b +	eor		v2.16b, v7.16b, v2.16b +	eor		v3.16b, v5.16b, v3.16b +	st1		{v0.16b-v3.16b}, [x0], #64 +	add		x5, x5, #INTERLEAVE +#endif +	cbz		w4, .LctroutNx +.LctrincNx: +	rev		x7, x5 +	ins		v4.d[1], x7 +	b		.LctrloopNx +.LctroutNx: +	sub		x5, x5, #1 +	rev		x7, x5 +	ins		v4.d[1], x7 +	b		.Lctrout +.Lctr1x: +	adds		w4, w4, #INTERLEAVE +	beq		.Lctrout +#endif +.Lctrloop: +	mov		v0.16b, v4.16b +	encrypt_block	v0, w3, x2, x6, w7 +	subs		w4, w4, #1 +	bmi		.Lctrhalfblock		/* blocks < 0 means 1/2 block */ +	ld1		{v3.16b}, [x1], #16 +	eor		v3.16b, v0.16b, v3.16b +	st1		{v3.16b}, [x0], #16 +	beq		.Lctrout +.Lctrinc: +	adds		x5, x5, #1		/* increment BE ctr */ +	rev		x7, x5 +	ins		v4.d[1], x7 +	bcc		.Lctrloop		/* no overflow? */ +	umov		x7, v4.d[0]		/* load upper word of ctr  */ +	rev		x7, x7			/* ... to handle the carry */ +	add		x7, x7, #1 +	rev		x7, x7 +	ins		v4.d[0], x7 +	b		.Lctrloop +.Lctrhalfblock: +	ld1		{v3.8b}, [x1] +	eor		v3.8b, v0.8b, v3.8b +	st1		{v3.8b}, [x0] +.Lctrout: +	FRAME_POP +	ret +AES_ENDPROC(aes_ctr_encrypt) +	.ltorg + + +	/* +	 * aes_xts_decrypt(u8 out[], u8 const in[], u8 const rk1[], int rounds, +	 *		   int blocks, u8 const rk2[], u8 iv[], int first) +	 * aes_xts_decrypt(u8 out[], u8 const in[], u8 const rk1[], int rounds, +	 *		   int blocks, u8 const rk2[], u8 iv[], int first) +	 */ + +	.macro		next_tweak, out, in, const, tmp +	sshr		\tmp\().2d,  \in\().2d,   #63 +	and		\tmp\().16b, \tmp\().16b, \const\().16b +	add		\out\().2d,  \in\().2d,   \in\().2d +	ext		\tmp\().16b, \tmp\().16b, \tmp\().16b, #8 +	eor		\out\().16b, \out\().16b, \tmp\().16b +	.endm + +.Lxts_mul_x: +	.word		1, 0, 0x87, 0 + +AES_ENTRY(aes_xts_encrypt) +	FRAME_PUSH +	cbz		w7, .LxtsencloopNx + +	ld1		{v4.16b}, [x6] +	enc_prepare	w3, x5, x6 +	encrypt_block	v4, w3, x5, x6, w7		/* first tweak */ +	enc_switch_key	w3, x2, x6 +	ldr		q7, .Lxts_mul_x +	b		.LxtsencNx + +.LxtsencloopNx: +	ldr		q7, .Lxts_mul_x +	next_tweak	v4, v4, v7, v8 +.LxtsencNx: +#if INTERLEAVE >= 2 +	subs		w4, w4, #INTERLEAVE +	bmi		.Lxtsenc1x +#if INTERLEAVE == 2 +	ld1		{v0.16b-v1.16b}, [x1], #32	/* get 2 pt blocks */ +	next_tweak	v5, v4, v7, v8 +	eor		v0.16b, v0.16b, v4.16b +	eor		v1.16b, v1.16b, v5.16b +	do_encrypt_block2x +	eor		v0.16b, v0.16b, v4.16b +	eor		v1.16b, v1.16b, v5.16b +	st1		{v0.16b-v1.16b}, [x0], #32 +	cbz		w4, .LxtsencoutNx +	next_tweak	v4, v5, v7, v8 +	b		.LxtsencNx +.LxtsencoutNx: +	mov		v4.16b, v5.16b +	b		.Lxtsencout +#else +	ld1		{v0.16b-v3.16b}, [x1], #64	/* get 4 pt blocks */ +	next_tweak	v5, v4, v7, v8 +	eor		v0.16b, v0.16b, v4.16b +	next_tweak	v6, v5, v7, v8 +	eor		v1.16b, v1.16b, v5.16b +	eor		v2.16b, v2.16b, v6.16b +	next_tweak	v7, v6, v7, v8 +	eor		v3.16b, v3.16b, v7.16b +	do_encrypt_block4x +	eor		v3.16b, v3.16b, v7.16b +	eor		v0.16b, v0.16b, v4.16b +	eor		v1.16b, v1.16b, v5.16b +	eor		v2.16b, v2.16b, v6.16b +	st1		{v0.16b-v3.16b}, [x0], #64 +	mov		v4.16b, v7.16b +	cbz		w4, .Lxtsencout +	b		.LxtsencloopNx +#endif +.Lxtsenc1x: +	adds		w4, w4, #INTERLEAVE +	beq		.Lxtsencout +#endif +.Lxtsencloop: +	ld1		{v1.16b}, [x1], #16 +	eor		v0.16b, v1.16b, v4.16b +	encrypt_block	v0, w3, x2, x6, w7 +	eor		v0.16b, v0.16b, v4.16b +	st1		{v0.16b}, [x0], #16 +	subs		w4, w4, #1 +	beq		.Lxtsencout +	next_tweak	v4, v4, v7, v8 +	b		.Lxtsencloop +.Lxtsencout: +	FRAME_POP +	ret +AES_ENDPROC(aes_xts_encrypt) + + +AES_ENTRY(aes_xts_decrypt) +	FRAME_PUSH +	cbz		w7, .LxtsdecloopNx + +	ld1		{v4.16b}, [x6] +	enc_prepare	w3, x5, x6 +	encrypt_block	v4, w3, x5, x6, w7		/* first tweak */ +	dec_prepare	w3, x2, x6 +	ldr		q7, .Lxts_mul_x +	b		.LxtsdecNx + +.LxtsdecloopNx: +	ldr		q7, .Lxts_mul_x +	next_tweak	v4, v4, v7, v8 +.LxtsdecNx: +#if INTERLEAVE >= 2 +	subs		w4, w4, #INTERLEAVE +	bmi		.Lxtsdec1x +#if INTERLEAVE == 2 +	ld1		{v0.16b-v1.16b}, [x1], #32	/* get 2 ct blocks */ +	next_tweak	v5, v4, v7, v8 +	eor		v0.16b, v0.16b, v4.16b +	eor		v1.16b, v1.16b, v5.16b +	do_decrypt_block2x +	eor		v0.16b, v0.16b, v4.16b +	eor		v1.16b, v1.16b, v5.16b +	st1		{v0.16b-v1.16b}, [x0], #32 +	cbz		w4, .LxtsdecoutNx +	next_tweak	v4, v5, v7, v8 +	b		.LxtsdecNx +.LxtsdecoutNx: +	mov		v4.16b, v5.16b +	b		.Lxtsdecout +#else +	ld1		{v0.16b-v3.16b}, [x1], #64	/* get 4 ct blocks */ +	next_tweak	v5, v4, v7, v8 +	eor		v0.16b, v0.16b, v4.16b +	next_tweak	v6, v5, v7, v8 +	eor		v1.16b, v1.16b, v5.16b +	eor		v2.16b, v2.16b, v6.16b +	next_tweak	v7, v6, v7, v8 +	eor		v3.16b, v3.16b, v7.16b +	do_decrypt_block4x +	eor		v3.16b, v3.16b, v7.16b +	eor		v0.16b, v0.16b, v4.16b +	eor		v1.16b, v1.16b, v5.16b +	eor		v2.16b, v2.16b, v6.16b +	st1		{v0.16b-v3.16b}, [x0], #64 +	mov		v4.16b, v7.16b +	cbz		w4, .Lxtsdecout +	b		.LxtsdecloopNx +#endif +.Lxtsdec1x: +	adds		w4, w4, #INTERLEAVE +	beq		.Lxtsdecout +#endif +.Lxtsdecloop: +	ld1		{v1.16b}, [x1], #16 +	eor		v0.16b, v1.16b, v4.16b +	decrypt_block	v0, w3, x2, x6, w7 +	eor		v0.16b, v0.16b, v4.16b +	st1		{v0.16b}, [x0], #16 +	subs		w4, w4, #1 +	beq		.Lxtsdecout +	next_tweak	v4, v4, v7, v8 +	b		.Lxtsdecloop +.Lxtsdecout: +	FRAME_POP +	ret +AES_ENDPROC(aes_xts_decrypt) diff --git a/arch/arm64/crypto/aes-neon.S b/arch/arm64/crypto/aes-neon.S new file mode 100644 index 00000000000..b93170e1cc9 --- /dev/null +++ b/arch/arm64/crypto/aes-neon.S @@ -0,0 +1,382 @@ +/* + * linux/arch/arm64/crypto/aes-neon.S - AES cipher for ARMv8 NEON + * + * Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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/linkage.h> + +#define AES_ENTRY(func)		ENTRY(neon_ ## func) +#define AES_ENDPROC(func)	ENDPROC(neon_ ## func) + +	/* multiply by polynomial 'x' in GF(2^8) */ +	.macro		mul_by_x, out, in, temp, const +	sshr		\temp, \in, #7 +	add		\out, \in, \in +	and		\temp, \temp, \const +	eor		\out, \out, \temp +	.endm + +	/* preload the entire Sbox */ +	.macro		prepare, sbox, shiftrows, temp +	adr		\temp, \sbox +	movi		v12.16b, #0x40 +	ldr		q13, \shiftrows +	movi		v14.16b, #0x1b +	ld1		{v16.16b-v19.16b}, [\temp], #64 +	ld1		{v20.16b-v23.16b}, [\temp], #64 +	ld1		{v24.16b-v27.16b}, [\temp], #64 +	ld1		{v28.16b-v31.16b}, [\temp] +	.endm + +	/* do preload for encryption */ +	.macro		enc_prepare, ignore0, ignore1, temp +	prepare		.LForward_Sbox, .LForward_ShiftRows, \temp +	.endm + +	.macro		enc_switch_key, ignore0, ignore1, temp +	/* do nothing */ +	.endm + +	/* do preload for decryption */ +	.macro		dec_prepare, ignore0, ignore1, temp +	prepare		.LReverse_Sbox, .LReverse_ShiftRows, \temp +	.endm + +	/* apply SubBytes transformation using the the preloaded Sbox */ +	.macro		sub_bytes, in +	sub		v9.16b, \in\().16b, v12.16b +	tbl		\in\().16b, {v16.16b-v19.16b}, \in\().16b +	sub		v10.16b, v9.16b, v12.16b +	tbx		\in\().16b, {v20.16b-v23.16b}, v9.16b +	sub		v11.16b, v10.16b, v12.16b +	tbx		\in\().16b, {v24.16b-v27.16b}, v10.16b +	tbx		\in\().16b, {v28.16b-v31.16b}, v11.16b +	.endm + +	/* apply MixColumns transformation */ +	.macro		mix_columns, in +	mul_by_x	v10.16b, \in\().16b, v9.16b, v14.16b +	rev32		v8.8h, \in\().8h +	eor		\in\().16b, v10.16b, \in\().16b +	shl		v9.4s, v8.4s, #24 +	shl		v11.4s, \in\().4s, #24 +	sri		v9.4s, v8.4s, #8 +	sri		v11.4s, \in\().4s, #8 +	eor		v9.16b, v9.16b, v8.16b +	eor		v10.16b, v10.16b, v9.16b +	eor		\in\().16b, v10.16b, v11.16b +	.endm + +	/* Inverse MixColumns: pre-multiply by { 5, 0, 4, 0 } */ +	.macro		inv_mix_columns, in +	mul_by_x	v11.16b, \in\().16b, v10.16b, v14.16b +	mul_by_x	v11.16b, v11.16b, v10.16b, v14.16b +	eor		\in\().16b, \in\().16b, v11.16b +	rev32		v11.8h, v11.8h +	eor		\in\().16b, \in\().16b, v11.16b +	mix_columns	\in +	.endm + +	.macro		do_block, enc, in, rounds, rk, rkp, i +	ld1		{v15.16b}, [\rk] +	add		\rkp, \rk, #16 +	mov		\i, \rounds +1111:	eor		\in\().16b, \in\().16b, v15.16b		/* ^round key */ +	tbl		\in\().16b, {\in\().16b}, v13.16b	/* ShiftRows */ +	sub_bytes	\in +	ld1		{v15.16b}, [\rkp], #16 +	subs		\i, \i, #1 +	beq		2222f +	.if		\enc == 1 +	mix_columns	\in +	.else +	inv_mix_columns	\in +	.endif +	b		1111b +2222:	eor		\in\().16b, \in\().16b, v15.16b		/* ^round key */ +	.endm + +	.macro		encrypt_block, in, rounds, rk, rkp, i +	do_block	1, \in, \rounds, \rk, \rkp, \i +	.endm + +	.macro		decrypt_block, in, rounds, rk, rkp, i +	do_block	0, \in, \rounds, \rk, \rkp, \i +	.endm + +	/* +	 * Interleaved versions: functionally equivalent to the +	 * ones above, but applied to 2 or 4 AES states in parallel. +	 */ + +	.macro		sub_bytes_2x, in0, in1 +	sub		v8.16b, \in0\().16b, v12.16b +	sub		v9.16b, \in1\().16b, v12.16b +	tbl		\in0\().16b, {v16.16b-v19.16b}, \in0\().16b +	tbl		\in1\().16b, {v16.16b-v19.16b}, \in1\().16b +	sub		v10.16b, v8.16b, v12.16b +	sub		v11.16b, v9.16b, v12.16b +	tbx		\in0\().16b, {v20.16b-v23.16b}, v8.16b +	tbx		\in1\().16b, {v20.16b-v23.16b}, v9.16b +	sub		v8.16b, v10.16b, v12.16b +	sub		v9.16b, v11.16b, v12.16b +	tbx		\in0\().16b, {v24.16b-v27.16b}, v10.16b +	tbx		\in1\().16b, {v24.16b-v27.16b}, v11.16b +	tbx		\in0\().16b, {v28.16b-v31.16b}, v8.16b +	tbx		\in1\().16b, {v28.16b-v31.16b}, v9.16b +	.endm + +	.macro		sub_bytes_4x, in0, in1, in2, in3 +	sub		v8.16b, \in0\().16b, v12.16b +	tbl		\in0\().16b, {v16.16b-v19.16b}, \in0\().16b +	sub		v9.16b, \in1\().16b, v12.16b +	tbl		\in1\().16b, {v16.16b-v19.16b}, \in1\().16b +	sub		v10.16b, \in2\().16b, v12.16b +	tbl		\in2\().16b, {v16.16b-v19.16b}, \in2\().16b +	sub		v11.16b, \in3\().16b, v12.16b +	tbl		\in3\().16b, {v16.16b-v19.16b}, \in3\().16b +	tbx		\in0\().16b, {v20.16b-v23.16b}, v8.16b +	tbx		\in1\().16b, {v20.16b-v23.16b}, v9.16b +	sub		v8.16b, v8.16b, v12.16b +	tbx		\in2\().16b, {v20.16b-v23.16b}, v10.16b +	sub		v9.16b, v9.16b, v12.16b +	tbx		\in3\().16b, {v20.16b-v23.16b}, v11.16b +	sub		v10.16b, v10.16b, v12.16b +	tbx		\in0\().16b, {v24.16b-v27.16b}, v8.16b +	sub		v11.16b, v11.16b, v12.16b +	tbx		\in1\().16b, {v24.16b-v27.16b}, v9.16b +	sub		v8.16b, v8.16b, v12.16b +	tbx		\in2\().16b, {v24.16b-v27.16b}, v10.16b +	sub		v9.16b, v9.16b, v12.16b +	tbx		\in3\().16b, {v24.16b-v27.16b}, v11.16b +	sub		v10.16b, v10.16b, v12.16b +	tbx		\in0\().16b, {v28.16b-v31.16b}, v8.16b +	sub		v11.16b, v11.16b, v12.16b +	tbx		\in1\().16b, {v28.16b-v31.16b}, v9.16b +	tbx		\in2\().16b, {v28.16b-v31.16b}, v10.16b +	tbx		\in3\().16b, {v28.16b-v31.16b}, v11.16b +	.endm + +	.macro		mul_by_x_2x, out0, out1, in0, in1, tmp0, tmp1, const +	sshr		\tmp0\().16b, \in0\().16b,  #7 +	add		\out0\().16b, \in0\().16b,  \in0\().16b +	sshr		\tmp1\().16b, \in1\().16b,  #7 +	and		\tmp0\().16b, \tmp0\().16b, \const\().16b +	add		\out1\().16b, \in1\().16b,  \in1\().16b +	and		\tmp1\().16b, \tmp1\().16b, \const\().16b +	eor		\out0\().16b, \out0\().16b, \tmp0\().16b +	eor		\out1\().16b, \out1\().16b, \tmp1\().16b +	.endm + +	.macro		mix_columns_2x, in0, in1 +	mul_by_x_2x	v8, v9, \in0, \in1, v10, v11, v14 +	rev32		v10.8h, \in0\().8h +	rev32		v11.8h, \in1\().8h +	eor		\in0\().16b, v8.16b, \in0\().16b +	eor		\in1\().16b, v9.16b, \in1\().16b +	shl		v12.4s, v10.4s, #24 +	shl		v13.4s, v11.4s, #24 +	eor		v8.16b, v8.16b, v10.16b +	sri		v12.4s, v10.4s, #8 +	shl		v10.4s, \in0\().4s, #24 +	eor		v9.16b, v9.16b, v11.16b +	sri		v13.4s, v11.4s, #8 +	shl		v11.4s, \in1\().4s, #24 +	sri		v10.4s, \in0\().4s, #8 +	eor		\in0\().16b, v8.16b, v12.16b +	sri		v11.4s, \in1\().4s, #8 +	eor		\in1\().16b, v9.16b, v13.16b +	eor		\in0\().16b, v10.16b, \in0\().16b +	eor		\in1\().16b, v11.16b, \in1\().16b +	.endm + +	.macro		inv_mix_cols_2x, in0, in1 +	mul_by_x_2x	v8, v9, \in0, \in1, v10, v11, v14 +	mul_by_x_2x	v8, v9, v8, v9, v10, v11, v14 +	eor		\in0\().16b, \in0\().16b, v8.16b +	eor		\in1\().16b, \in1\().16b, v9.16b +	rev32		v8.8h, v8.8h +	rev32		v9.8h, v9.8h +	eor		\in0\().16b, \in0\().16b, v8.16b +	eor		\in1\().16b, \in1\().16b, v9.16b +	mix_columns_2x	\in0, \in1 +	.endm + +	.macro		inv_mix_cols_4x, in0, in1, in2, in3 +	mul_by_x_2x	v8, v9, \in0, \in1, v10, v11, v14 +	mul_by_x_2x	v10, v11, \in2, \in3, v12, v13, v14 +	mul_by_x_2x	v8, v9, v8, v9, v12, v13, v14 +	mul_by_x_2x	v10, v11, v10, v11, v12, v13, v14 +	eor		\in0\().16b, \in0\().16b, v8.16b +	eor		\in1\().16b, \in1\().16b, v9.16b +	eor		\in2\().16b, \in2\().16b, v10.16b +	eor		\in3\().16b, \in3\().16b, v11.16b +	rev32		v8.8h, v8.8h +	rev32		v9.8h, v9.8h +	rev32		v10.8h, v10.8h +	rev32		v11.8h, v11.8h +	eor		\in0\().16b, \in0\().16b, v8.16b +	eor		\in1\().16b, \in1\().16b, v9.16b +	eor		\in2\().16b, \in2\().16b, v10.16b +	eor		\in3\().16b, \in3\().16b, v11.16b +	mix_columns_2x	\in0, \in1 +	mix_columns_2x	\in2, \in3 +	.endm + +	.macro		do_block_2x, enc, in0, in1 rounds, rk, rkp, i +	ld1		{v15.16b}, [\rk] +	add		\rkp, \rk, #16 +	mov		\i, \rounds +1111:	eor		\in0\().16b, \in0\().16b, v15.16b	/* ^round key */ +	eor		\in1\().16b, \in1\().16b, v15.16b	/* ^round key */ +	sub_bytes_2x	\in0, \in1 +	tbl		\in0\().16b, {\in0\().16b}, v13.16b	/* ShiftRows */ +	tbl		\in1\().16b, {\in1\().16b}, v13.16b	/* ShiftRows */ +	ld1		{v15.16b}, [\rkp], #16 +	subs		\i, \i, #1 +	beq		2222f +	.if		\enc == 1 +	mix_columns_2x	\in0, \in1 +	ldr		q13, .LForward_ShiftRows +	.else +	inv_mix_cols_2x	\in0, \in1 +	ldr		q13, .LReverse_ShiftRows +	.endif +	movi		v12.16b, #0x40 +	b		1111b +2222:	eor		\in0\().16b, \in0\().16b, v15.16b	/* ^round key */ +	eor		\in1\().16b, \in1\().16b, v15.16b	/* ^round key */ +	.endm + +	.macro		do_block_4x, enc, in0, in1, in2, in3, rounds, rk, rkp, i +	ld1		{v15.16b}, [\rk] +	add		\rkp, \rk, #16 +	mov		\i, \rounds +1111:	eor		\in0\().16b, \in0\().16b, v15.16b	/* ^round key */ +	eor		\in1\().16b, \in1\().16b, v15.16b	/* ^round key */ +	eor		\in2\().16b, \in2\().16b, v15.16b	/* ^round key */ +	eor		\in3\().16b, \in3\().16b, v15.16b	/* ^round key */ +	sub_bytes_4x	\in0, \in1, \in2, \in3 +	tbl		\in0\().16b, {\in0\().16b}, v13.16b	/* ShiftRows */ +	tbl		\in1\().16b, {\in1\().16b}, v13.16b	/* ShiftRows */ +	tbl		\in2\().16b, {\in2\().16b}, v13.16b	/* ShiftRows */ +	tbl		\in3\().16b, {\in3\().16b}, v13.16b	/* ShiftRows */ +	ld1		{v15.16b}, [\rkp], #16 +	subs		\i, \i, #1 +	beq		2222f +	.if		\enc == 1 +	mix_columns_2x	\in0, \in1 +	mix_columns_2x	\in2, \in3 +	ldr		q13, .LForward_ShiftRows +	.else +	inv_mix_cols_4x	\in0, \in1, \in2, \in3 +	ldr		q13, .LReverse_ShiftRows +	.endif +	movi		v12.16b, #0x40 +	b		1111b +2222:	eor		\in0\().16b, \in0\().16b, v15.16b	/* ^round key */ +	eor		\in1\().16b, \in1\().16b, v15.16b	/* ^round key */ +	eor		\in2\().16b, \in2\().16b, v15.16b	/* ^round key */ +	eor		\in3\().16b, \in3\().16b, v15.16b	/* ^round key */ +	.endm + +	.macro		encrypt_block2x, in0, in1, rounds, rk, rkp, i +	do_block_2x	1, \in0, \in1, \rounds, \rk, \rkp, \i +	.endm + +	.macro		decrypt_block2x, in0, in1, rounds, rk, rkp, i +	do_block_2x	0, \in0, \in1, \rounds, \rk, \rkp, \i +	.endm + +	.macro		encrypt_block4x, in0, in1, in2, in3, rounds, rk, rkp, i +	do_block_4x	1, \in0, \in1, \in2, \in3, \rounds, \rk, \rkp, \i +	.endm + +	.macro		decrypt_block4x, in0, in1, in2, in3, rounds, rk, rkp, i +	do_block_4x	0, \in0, \in1, \in2, \in3, \rounds, \rk, \rkp, \i +	.endm + +#include "aes-modes.S" + +	.text +	.align		4 +.LForward_ShiftRows: +	.byte		0x0, 0x5, 0xa, 0xf, 0x4, 0x9, 0xe, 0x3 +	.byte		0x8, 0xd, 0x2, 0x7, 0xc, 0x1, 0x6, 0xb + +.LReverse_ShiftRows: +	.byte		0x0, 0xd, 0xa, 0x7, 0x4, 0x1, 0xe, 0xb +	.byte		0x8, 0x5, 0x2, 0xf, 0xc, 0x9, 0x6, 0x3 + +.LForward_Sbox: +	.byte		0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5 +	.byte		0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76 +	.byte		0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0 +	.byte		0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0 +	.byte		0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc +	.byte		0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15 +	.byte		0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a +	.byte		0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75 +	.byte		0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0 +	.byte		0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84 +	.byte		0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b +	.byte		0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf +	.byte		0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85 +	.byte		0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8 +	.byte		0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5 +	.byte		0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2 +	.byte		0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17 +	.byte		0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73 +	.byte		0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88 +	.byte		0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb +	.byte		0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c +	.byte		0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79 +	.byte		0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9 +	.byte		0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08 +	.byte		0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6 +	.byte		0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a +	.byte		0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e +	.byte		0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e +	.byte		0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94 +	.byte		0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf +	.byte		0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68 +	.byte		0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 + +.LReverse_Sbox: +	.byte		0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38 +	.byte		0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb +	.byte		0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87 +	.byte		0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb +	.byte		0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d +	.byte		0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e +	.byte		0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2 +	.byte		0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25 +	.byte		0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16 +	.byte		0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92 +	.byte		0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda +	.byte		0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84 +	.byte		0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a +	.byte		0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06 +	.byte		0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02 +	.byte		0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b +	.byte		0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea +	.byte		0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73 +	.byte		0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85 +	.byte		0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e +	.byte		0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89 +	.byte		0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b +	.byte		0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20 +	.byte		0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4 +	.byte		0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31 +	.byte		0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f +	.byte		0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d +	.byte		0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef +	.byte		0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0 +	.byte		0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61 +	.byte		0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26 +	.byte		0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d diff --git a/arch/arm64/crypto/ghash-ce-core.S b/arch/arm64/crypto/ghash-ce-core.S new file mode 100644 index 00000000000..dc457015884 --- /dev/null +++ b/arch/arm64/crypto/ghash-ce-core.S @@ -0,0 +1,79 @@ +/* + * Accelerated GHASH implementation with ARMv8 PMULL instructions. + * + * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org> + * + * 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/linkage.h> +#include <asm/assembler.h> + +	SHASH	.req	v0 +	SHASH2	.req	v1 +	T1	.req	v2 +	T2	.req	v3 +	MASK	.req	v4 +	XL	.req	v5 +	XM	.req	v6 +	XH	.req	v7 +	IN1	.req	v7 + +	.text +	.arch		armv8-a+crypto + +	/* +	 * void pmull_ghash_update(int blocks, u64 dg[], const char *src, +	 *			   struct ghash_key const *k, const char *head) +	 */ +ENTRY(pmull_ghash_update) +	ld1		{SHASH.16b}, [x3] +	ld1		{XL.16b}, [x1] +	movi		MASK.16b, #0xe1 +	ext		SHASH2.16b, SHASH.16b, SHASH.16b, #8 +	shl		MASK.2d, MASK.2d, #57 +	eor		SHASH2.16b, SHASH2.16b, SHASH.16b + +	/* do the head block first, if supplied */ +	cbz		x4, 0f +	ld1		{T1.2d}, [x4] +	b		1f + +0:	ld1		{T1.2d}, [x2], #16 +	sub		w0, w0, #1 + +1:	/* multiply XL by SHASH in GF(2^128) */ +CPU_LE(	rev64		T1.16b, T1.16b	) + +	ext		T2.16b, XL.16b, XL.16b, #8 +	ext		IN1.16b, T1.16b, T1.16b, #8 +	eor		T1.16b, T1.16b, T2.16b +	eor		XL.16b, XL.16b, IN1.16b + +	pmull2		XH.1q, SHASH.2d, XL.2d		// a1 * b1 +	eor		T1.16b, T1.16b, XL.16b +	pmull		XL.1q, SHASH.1d, XL.1d		// a0 * b0 +	pmull		XM.1q, SHASH2.1d, T1.1d		// (a1 + a0)(b1 + b0) + +	ext		T1.16b, XL.16b, XH.16b, #8 +	eor		T2.16b, XL.16b, XH.16b +	eor		XM.16b, XM.16b, T1.16b +	eor		XM.16b, XM.16b, T2.16b +	pmull		T2.1q, XL.1d, MASK.1d + +	mov		XH.d[0], XM.d[1] +	mov		XM.d[1], XL.d[0] + +	eor		XL.16b, XM.16b, T2.16b +	ext		T2.16b, XL.16b, XL.16b, #8 +	pmull		XL.1q, XL.1d, MASK.1d +	eor		T2.16b, T2.16b, XH.16b +	eor		XL.16b, XL.16b, T2.16b + +	cbnz		w0, 0b + +	st1		{XL.16b}, [x1] +	ret +ENDPROC(pmull_ghash_update) diff --git a/arch/arm64/crypto/ghash-ce-glue.c b/arch/arm64/crypto/ghash-ce-glue.c new file mode 100644 index 00000000000..833ec1e3f3e --- /dev/null +++ b/arch/arm64/crypto/ghash-ce-glue.c @@ -0,0 +1,156 @@ +/* + * Accelerated GHASH implementation with ARMv8 PMULL instructions. + * + * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org> + * + * 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 <asm/neon.h> +#include <asm/unaligned.h> +#include <crypto/internal/hash.h> +#include <linux/cpufeature.h> +#include <linux/crypto.h> +#include <linux/module.h> + +MODULE_DESCRIPTION("GHASH secure hash using ARMv8 Crypto Extensions"); +MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>"); +MODULE_LICENSE("GPL v2"); + +#define GHASH_BLOCK_SIZE	16 +#define GHASH_DIGEST_SIZE	16 + +struct ghash_key { +	u64 a; +	u64 b; +}; + +struct ghash_desc_ctx { +	u64 digest[GHASH_DIGEST_SIZE/sizeof(u64)]; +	u8 buf[GHASH_BLOCK_SIZE]; +	u32 count; +}; + +asmlinkage void pmull_ghash_update(int blocks, u64 dg[], const char *src, +				   struct ghash_key const *k, const char *head); + +static int ghash_init(struct shash_desc *desc) +{ +	struct ghash_desc_ctx *ctx = shash_desc_ctx(desc); + +	*ctx = (struct ghash_desc_ctx){}; +	return 0; +} + +static int ghash_update(struct shash_desc *desc, const u8 *src, +			unsigned int len) +{ +	struct ghash_desc_ctx *ctx = shash_desc_ctx(desc); +	unsigned int partial = ctx->count % GHASH_BLOCK_SIZE; + +	ctx->count += len; + +	if ((partial + len) >= GHASH_BLOCK_SIZE) { +		struct ghash_key *key = crypto_shash_ctx(desc->tfm); +		int blocks; + +		if (partial) { +			int p = GHASH_BLOCK_SIZE - partial; + +			memcpy(ctx->buf + partial, src, p); +			src += p; +			len -= p; +		} + +		blocks = len / GHASH_BLOCK_SIZE; +		len %= GHASH_BLOCK_SIZE; + +		kernel_neon_begin_partial(8); +		pmull_ghash_update(blocks, ctx->digest, src, key, +				   partial ? ctx->buf : NULL); +		kernel_neon_end(); +		src += blocks * GHASH_BLOCK_SIZE; +		partial = 0; +	} +	if (len) +		memcpy(ctx->buf + partial, src, len); +	return 0; +} + +static int ghash_final(struct shash_desc *desc, u8 *dst) +{ +	struct ghash_desc_ctx *ctx = shash_desc_ctx(desc); +	unsigned int partial = ctx->count % GHASH_BLOCK_SIZE; + +	if (partial) { +		struct ghash_key *key = crypto_shash_ctx(desc->tfm); + +		memset(ctx->buf + partial, 0, GHASH_BLOCK_SIZE - partial); + +		kernel_neon_begin_partial(8); +		pmull_ghash_update(1, ctx->digest, ctx->buf, key, NULL); +		kernel_neon_end(); +	} +	put_unaligned_be64(ctx->digest[1], dst); +	put_unaligned_be64(ctx->digest[0], dst + 8); + +	*ctx = (struct ghash_desc_ctx){}; +	return 0; +} + +static int ghash_setkey(struct crypto_shash *tfm, +			const u8 *inkey, unsigned int keylen) +{ +	struct ghash_key *key = crypto_shash_ctx(tfm); +	u64 a, b; + +	if (keylen != GHASH_BLOCK_SIZE) { +		crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); +		return -EINVAL; +	} + +	/* perform multiplication by 'x' in GF(2^128) */ +	b = get_unaligned_be64(inkey); +	a = get_unaligned_be64(inkey + 8); + +	key->a = (a << 1) | (b >> 63); +	key->b = (b << 1) | (a >> 63); + +	if (b >> 63) +		key->b ^= 0xc200000000000000UL; + +	return 0; +} + +static struct shash_alg ghash_alg = { +	.digestsize	= GHASH_DIGEST_SIZE, +	.init		= ghash_init, +	.update		= ghash_update, +	.final		= ghash_final, +	.setkey		= ghash_setkey, +	.descsize	= sizeof(struct ghash_desc_ctx), +	.base		= { +		.cra_name		= "ghash", +		.cra_driver_name	= "ghash-ce", +		.cra_priority		= 200, +		.cra_flags		= CRYPTO_ALG_TYPE_SHASH, +		.cra_blocksize		= GHASH_BLOCK_SIZE, +		.cra_ctxsize		= sizeof(struct ghash_key), +		.cra_module		= THIS_MODULE, +	}, +}; + +static int __init ghash_ce_mod_init(void) +{ +	return crypto_register_shash(&ghash_alg); +} + +static void __exit ghash_ce_mod_exit(void) +{ +	crypto_unregister_shash(&ghash_alg); +} + +module_cpu_feature_match(PMULL, ghash_ce_mod_init); +module_exit(ghash_ce_mod_exit); diff --git a/arch/arm64/crypto/sha1-ce-core.S b/arch/arm64/crypto/sha1-ce-core.S new file mode 100644 index 00000000000..09d57d98609 --- /dev/null +++ b/arch/arm64/crypto/sha1-ce-core.S @@ -0,0 +1,153 @@ +/* + * sha1-ce-core.S - SHA-1 secure hash using ARMv8 Crypto Extensions + * + * Copyright (C) 2014 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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/linkage.h> +#include <asm/assembler.h> + +	.text +	.arch		armv8-a+crypto + +	k0		.req	v0 +	k1		.req	v1 +	k2		.req	v2 +	k3		.req	v3 + +	t0		.req	v4 +	t1		.req	v5 + +	dga		.req	q6 +	dgav		.req	v6 +	dgb		.req	s7 +	dgbv		.req	v7 + +	dg0q		.req	q12 +	dg0s		.req	s12 +	dg0v		.req	v12 +	dg1s		.req	s13 +	dg1v		.req	v13 +	dg2s		.req	s14 + +	.macro		add_only, op, ev, rc, s0, dg1 +	.ifc		\ev, ev +	add		t1.4s, v\s0\().4s, \rc\().4s +	sha1h		dg2s, dg0s +	.ifnb		\dg1 +	sha1\op		dg0q, \dg1, t0.4s +	.else +	sha1\op		dg0q, dg1s, t0.4s +	.endif +	.else +	.ifnb		\s0 +	add		t0.4s, v\s0\().4s, \rc\().4s +	.endif +	sha1h		dg1s, dg0s +	sha1\op		dg0q, dg2s, t1.4s +	.endif +	.endm + +	.macro		add_update, op, ev, rc, s0, s1, s2, s3, dg1 +	sha1su0		v\s0\().4s, v\s1\().4s, v\s2\().4s +	add_only	\op, \ev, \rc, \s1, \dg1 +	sha1su1		v\s0\().4s, v\s3\().4s +	.endm + +	/* +	 * The SHA1 round constants +	 */ +	.align		4 +.Lsha1_rcon: +	.word		0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 + +	/* +	 * void sha1_ce_transform(int blocks, u8 const *src, u32 *state, +	 * 			  u8 *head, long bytes) +	 */ +ENTRY(sha1_ce_transform) +	/* load round constants */ +	adr		x6, .Lsha1_rcon +	ld1r		{k0.4s}, [x6], #4 +	ld1r		{k1.4s}, [x6], #4 +	ld1r		{k2.4s}, [x6], #4 +	ld1r		{k3.4s}, [x6] + +	/* load state */ +	ldr		dga, [x2] +	ldr		dgb, [x2, #16] + +	/* load partial state (if supplied) */ +	cbz		x3, 0f +	ld1		{v8.4s-v11.4s}, [x3] +	b		1f + +	/* load input */ +0:	ld1		{v8.4s-v11.4s}, [x1], #64 +	sub		w0, w0, #1 + +1: +CPU_LE(	rev32		v8.16b, v8.16b		) +CPU_LE(	rev32		v9.16b, v9.16b		) +CPU_LE(	rev32		v10.16b, v10.16b	) +CPU_LE(	rev32		v11.16b, v11.16b	) + +2:	add		t0.4s, v8.4s, k0.4s +	mov		dg0v.16b, dgav.16b + +	add_update	c, ev, k0,  8,  9, 10, 11, dgb +	add_update	c, od, k0,  9, 10, 11,  8 +	add_update	c, ev, k0, 10, 11,  8,  9 +	add_update	c, od, k0, 11,  8,  9, 10 +	add_update	c, ev, k1,  8,  9, 10, 11 + +	add_update	p, od, k1,  9, 10, 11,  8 +	add_update	p, ev, k1, 10, 11,  8,  9 +	add_update	p, od, k1, 11,  8,  9, 10 +	add_update	p, ev, k1,  8,  9, 10, 11 +	add_update	p, od, k2,  9, 10, 11,  8 + +	add_update	m, ev, k2, 10, 11,  8,  9 +	add_update	m, od, k2, 11,  8,  9, 10 +	add_update	m, ev, k2,  8,  9, 10, 11 +	add_update	m, od, k2,  9, 10, 11,  8 +	add_update	m, ev, k3, 10, 11,  8,  9 + +	add_update	p, od, k3, 11,  8,  9, 10 +	add_only	p, ev, k3,  9 +	add_only	p, od, k3, 10 +	add_only	p, ev, k3, 11 +	add_only	p, od + +	/* update state */ +	add		dgbv.2s, dgbv.2s, dg1v.2s +	add		dgav.4s, dgav.4s, dg0v.4s + +	cbnz		w0, 0b + +	/* +	 * Final block: add padding and total bit count. +	 * Skip if we have no total byte count in x4. In that case, the input +	 * size was not a round multiple of the block size, and the padding is +	 * handled by the C code. +	 */ +	cbz		x4, 3f +	movi		v9.2d, #0 +	mov		x8, #0x80000000 +	movi		v10.2d, #0 +	ror		x7, x4, #29		// ror(lsl(x4, 3), 32) +	fmov		d8, x8 +	mov		x4, #0 +	mov		v11.d[0], xzr +	mov		v11.d[1], x7 +	b		2b + +	/* store new state */ +3:	str		dga, [x2] +	str		dgb, [x2, #16] +	ret +ENDPROC(sha1_ce_transform) diff --git a/arch/arm64/crypto/sha1-ce-glue.c b/arch/arm64/crypto/sha1-ce-glue.c new file mode 100644 index 00000000000..6fe83f37a75 --- /dev/null +++ b/arch/arm64/crypto/sha1-ce-glue.c @@ -0,0 +1,174 @@ +/* + * sha1-ce-glue.c - SHA-1 secure hash using ARMv8 Crypto Extensions + * + * Copyright (C) 2014 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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 <asm/neon.h> +#include <asm/unaligned.h> +#include <crypto/internal/hash.h> +#include <crypto/sha.h> +#include <linux/cpufeature.h> +#include <linux/crypto.h> +#include <linux/module.h> + +MODULE_DESCRIPTION("SHA1 secure hash using ARMv8 Crypto Extensions"); +MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>"); +MODULE_LICENSE("GPL v2"); + +asmlinkage void sha1_ce_transform(int blocks, u8 const *src, u32 *state, +				  u8 *head, long bytes); + +static int sha1_init(struct shash_desc *desc) +{ +	struct sha1_state *sctx = shash_desc_ctx(desc); + +	*sctx = (struct sha1_state){ +		.state = { SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4 }, +	}; +	return 0; +} + +static int sha1_update(struct shash_desc *desc, const u8 *data, +		       unsigned int len) +{ +	struct sha1_state *sctx = shash_desc_ctx(desc); +	unsigned int partial = sctx->count % SHA1_BLOCK_SIZE; + +	sctx->count += len; + +	if ((partial + len) >= SHA1_BLOCK_SIZE) { +		int blocks; + +		if (partial) { +			int p = SHA1_BLOCK_SIZE - partial; + +			memcpy(sctx->buffer + partial, data, p); +			data += p; +			len -= p; +		} + +		blocks = len / SHA1_BLOCK_SIZE; +		len %= SHA1_BLOCK_SIZE; + +		kernel_neon_begin_partial(16); +		sha1_ce_transform(blocks, data, sctx->state, +				  partial ? sctx->buffer : NULL, 0); +		kernel_neon_end(); + +		data += blocks * SHA1_BLOCK_SIZE; +		partial = 0; +	} +	if (len) +		memcpy(sctx->buffer + partial, data, len); +	return 0; +} + +static int sha1_final(struct shash_desc *desc, u8 *out) +{ +	static const u8 padding[SHA1_BLOCK_SIZE] = { 0x80, }; + +	struct sha1_state *sctx = shash_desc_ctx(desc); +	__be64 bits = cpu_to_be64(sctx->count << 3); +	__be32 *dst = (__be32 *)out; +	int i; + +	u32 padlen = SHA1_BLOCK_SIZE +		     - ((sctx->count + sizeof(bits)) % SHA1_BLOCK_SIZE); + +	sha1_update(desc, padding, padlen); +	sha1_update(desc, (const u8 *)&bits, sizeof(bits)); + +	for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(__be32); i++) +		put_unaligned_be32(sctx->state[i], dst++); + +	*sctx = (struct sha1_state){}; +	return 0; +} + +static int sha1_finup(struct shash_desc *desc, const u8 *data, +		      unsigned int len, u8 *out) +{ +	struct sha1_state *sctx = shash_desc_ctx(desc); +	__be32 *dst = (__be32 *)out; +	int blocks; +	int i; + +	if (sctx->count || !len || (len % SHA1_BLOCK_SIZE)) { +		sha1_update(desc, data, len); +		return sha1_final(desc, out); +	} + +	/* +	 * Use a fast path if the input is a multiple of 64 bytes. In +	 * this case, there is no need to copy data around, and we can +	 * perform the entire digest calculation in a single invocation +	 * of sha1_ce_transform() +	 */ +	blocks = len / SHA1_BLOCK_SIZE; + +	kernel_neon_begin_partial(16); +	sha1_ce_transform(blocks, data, sctx->state, NULL, len); +	kernel_neon_end(); + +	for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(__be32); i++) +		put_unaligned_be32(sctx->state[i], dst++); + +	*sctx = (struct sha1_state){}; +	return 0; +} + +static int sha1_export(struct shash_desc *desc, void *out) +{ +	struct sha1_state *sctx = shash_desc_ctx(desc); +	struct sha1_state *dst = out; + +	*dst = *sctx; +	return 0; +} + +static int sha1_import(struct shash_desc *desc, const void *in) +{ +	struct sha1_state *sctx = shash_desc_ctx(desc); +	struct sha1_state const *src = in; + +	*sctx = *src; +	return 0; +} + +static struct shash_alg alg = { +	.init			= sha1_init, +	.update			= sha1_update, +	.final			= sha1_final, +	.finup			= sha1_finup, +	.export			= sha1_export, +	.import			= sha1_import, +	.descsize		= sizeof(struct sha1_state), +	.digestsize		= SHA1_DIGEST_SIZE, +	.statesize		= sizeof(struct sha1_state), +	.base			= { +		.cra_name		= "sha1", +		.cra_driver_name	= "sha1-ce", +		.cra_priority		= 200, +		.cra_flags		= CRYPTO_ALG_TYPE_SHASH, +		.cra_blocksize		= SHA1_BLOCK_SIZE, +		.cra_module		= THIS_MODULE, +	} +}; + +static int __init sha1_ce_mod_init(void) +{ +	return crypto_register_shash(&alg); +} + +static void __exit sha1_ce_mod_fini(void) +{ +	crypto_unregister_shash(&alg); +} + +module_cpu_feature_match(SHA1, sha1_ce_mod_init); +module_exit(sha1_ce_mod_fini); diff --git a/arch/arm64/crypto/sha2-ce-core.S b/arch/arm64/crypto/sha2-ce-core.S new file mode 100644 index 00000000000..7f29fc031ea --- /dev/null +++ b/arch/arm64/crypto/sha2-ce-core.S @@ -0,0 +1,156 @@ +/* + * sha2-ce-core.S - core SHA-224/SHA-256 transform using v8 Crypto Extensions + * + * Copyright (C) 2014 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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/linkage.h> +#include <asm/assembler.h> + +	.text +	.arch		armv8-a+crypto + +	dga		.req	q20 +	dgav		.req	v20 +	dgb		.req	q21 +	dgbv		.req	v21 + +	t0		.req	v22 +	t1		.req	v23 + +	dg0q		.req	q24 +	dg0v		.req	v24 +	dg1q		.req	q25 +	dg1v		.req	v25 +	dg2q		.req	q26 +	dg2v		.req	v26 + +	.macro		add_only, ev, rc, s0 +	mov		dg2v.16b, dg0v.16b +	.ifeq		\ev +	add		t1.4s, v\s0\().4s, \rc\().4s +	sha256h		dg0q, dg1q, t0.4s +	sha256h2	dg1q, dg2q, t0.4s +	.else +	.ifnb		\s0 +	add		t0.4s, v\s0\().4s, \rc\().4s +	.endif +	sha256h		dg0q, dg1q, t1.4s +	sha256h2	dg1q, dg2q, t1.4s +	.endif +	.endm + +	.macro		add_update, ev, rc, s0, s1, s2, s3 +	sha256su0	v\s0\().4s, v\s1\().4s +	add_only	\ev, \rc, \s1 +	sha256su1	v\s0\().4s, v\s2\().4s, v\s3\().4s +	.endm + +	/* +	 * The SHA-256 round constants +	 */ +	.align		4 +.Lsha2_rcon: +	.word		0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5 +	.word		0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5 +	.word		0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3 +	.word		0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174 +	.word		0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc +	.word		0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da +	.word		0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7 +	.word		0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967 +	.word		0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13 +	.word		0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85 +	.word		0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3 +	.word		0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070 +	.word		0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5 +	.word		0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3 +	.word		0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208 +	.word		0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + +	/* +	 * void sha2_ce_transform(int blocks, u8 const *src, u32 *state, +	 *                        u8 *head, long bytes) +	 */ +ENTRY(sha2_ce_transform) +	/* load round constants */ +	adr		x8, .Lsha2_rcon +	ld1		{ v0.4s- v3.4s}, [x8], #64 +	ld1		{ v4.4s- v7.4s}, [x8], #64 +	ld1		{ v8.4s-v11.4s}, [x8], #64 +	ld1		{v12.4s-v15.4s}, [x8] + +	/* load state */ +	ldp		dga, dgb, [x2] + +	/* load partial input (if supplied) */ +	cbz		x3, 0f +	ld1		{v16.4s-v19.4s}, [x3] +	b		1f + +	/* load input */ +0:	ld1		{v16.4s-v19.4s}, [x1], #64 +	sub		w0, w0, #1 + +1: +CPU_LE(	rev32		v16.16b, v16.16b	) +CPU_LE(	rev32		v17.16b, v17.16b	) +CPU_LE(	rev32		v18.16b, v18.16b	) +CPU_LE(	rev32		v19.16b, v19.16b	) + +2:	add		t0.4s, v16.4s, v0.4s +	mov		dg0v.16b, dgav.16b +	mov		dg1v.16b, dgbv.16b + +	add_update	0,  v1, 16, 17, 18, 19 +	add_update	1,  v2, 17, 18, 19, 16 +	add_update	0,  v3, 18, 19, 16, 17 +	add_update	1,  v4, 19, 16, 17, 18 + +	add_update	0,  v5, 16, 17, 18, 19 +	add_update	1,  v6, 17, 18, 19, 16 +	add_update	0,  v7, 18, 19, 16, 17 +	add_update	1,  v8, 19, 16, 17, 18 + +	add_update	0,  v9, 16, 17, 18, 19 +	add_update	1, v10, 17, 18, 19, 16 +	add_update	0, v11, 18, 19, 16, 17 +	add_update	1, v12, 19, 16, 17, 18 + +	add_only	0, v13, 17 +	add_only	1, v14, 18 +	add_only	0, v15, 19 +	add_only	1 + +	/* update state */ +	add		dgav.4s, dgav.4s, dg0v.4s +	add		dgbv.4s, dgbv.4s, dg1v.4s + +	/* handled all input blocks? */ +	cbnz		w0, 0b + +	/* +	 * Final block: add padding and total bit count. +	 * Skip if we have no total byte count in x4. In that case, the input +	 * size was not a round multiple of the block size, and the padding is +	 * handled by the C code. +	 */ +	cbz		x4, 3f +	movi		v17.2d, #0 +	mov		x8, #0x80000000 +	movi		v18.2d, #0 +	ror		x7, x4, #29		// ror(lsl(x4, 3), 32) +	fmov		d16, x8 +	mov		x4, #0 +	mov		v19.d[0], xzr +	mov		v19.d[1], x7 +	b		2b + +	/* store new state */ +3:	stp		dga, dgb, [x2] +	ret +ENDPROC(sha2_ce_transform) diff --git a/arch/arm64/crypto/sha2-ce-glue.c b/arch/arm64/crypto/sha2-ce-glue.c new file mode 100644 index 00000000000..c294e67d392 --- /dev/null +++ b/arch/arm64/crypto/sha2-ce-glue.c @@ -0,0 +1,255 @@ +/* + * sha2-ce-glue.c - SHA-224/SHA-256 using ARMv8 Crypto Extensions + * + * Copyright (C) 2014 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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 <asm/neon.h> +#include <asm/unaligned.h> +#include <crypto/internal/hash.h> +#include <crypto/sha.h> +#include <linux/cpufeature.h> +#include <linux/crypto.h> +#include <linux/module.h> + +MODULE_DESCRIPTION("SHA-224/SHA-256 secure hash using ARMv8 Crypto Extensions"); +MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>"); +MODULE_LICENSE("GPL v2"); + +asmlinkage int sha2_ce_transform(int blocks, u8 const *src, u32 *state, +				 u8 *head, long bytes); + +static int sha224_init(struct shash_desc *desc) +{ +	struct sha256_state *sctx = shash_desc_ctx(desc); + +	*sctx = (struct sha256_state){ +		.state = { +			SHA224_H0, SHA224_H1, SHA224_H2, SHA224_H3, +			SHA224_H4, SHA224_H5, SHA224_H6, SHA224_H7, +		} +	}; +	return 0; +} + +static int sha256_init(struct shash_desc *desc) +{ +	struct sha256_state *sctx = shash_desc_ctx(desc); + +	*sctx = (struct sha256_state){ +		.state = { +			SHA256_H0, SHA256_H1, SHA256_H2, SHA256_H3, +			SHA256_H4, SHA256_H5, SHA256_H6, SHA256_H7, +		} +	}; +	return 0; +} + +static int sha2_update(struct shash_desc *desc, const u8 *data, +		       unsigned int len) +{ +	struct sha256_state *sctx = shash_desc_ctx(desc); +	unsigned int partial = sctx->count % SHA256_BLOCK_SIZE; + +	sctx->count += len; + +	if ((partial + len) >= SHA256_BLOCK_SIZE) { +		int blocks; + +		if (partial) { +			int p = SHA256_BLOCK_SIZE - partial; + +			memcpy(sctx->buf + partial, data, p); +			data += p; +			len -= p; +		} + +		blocks = len / SHA256_BLOCK_SIZE; +		len %= SHA256_BLOCK_SIZE; + +		kernel_neon_begin_partial(28); +		sha2_ce_transform(blocks, data, sctx->state, +				  partial ? sctx->buf : NULL, 0); +		kernel_neon_end(); + +		data += blocks * SHA256_BLOCK_SIZE; +		partial = 0; +	} +	if (len) +		memcpy(sctx->buf + partial, data, len); +	return 0; +} + +static void sha2_final(struct shash_desc *desc) +{ +	static const u8 padding[SHA256_BLOCK_SIZE] = { 0x80, }; + +	struct sha256_state *sctx = shash_desc_ctx(desc); +	__be64 bits = cpu_to_be64(sctx->count << 3); +	u32 padlen = SHA256_BLOCK_SIZE +		     - ((sctx->count + sizeof(bits)) % SHA256_BLOCK_SIZE); + +	sha2_update(desc, padding, padlen); +	sha2_update(desc, (const u8 *)&bits, sizeof(bits)); +} + +static int sha224_final(struct shash_desc *desc, u8 *out) +{ +	struct sha256_state *sctx = shash_desc_ctx(desc); +	__be32 *dst = (__be32 *)out; +	int i; + +	sha2_final(desc); + +	for (i = 0; i < SHA224_DIGEST_SIZE / sizeof(__be32); i++) +		put_unaligned_be32(sctx->state[i], dst++); + +	*sctx = (struct sha256_state){}; +	return 0; +} + +static int sha256_final(struct shash_desc *desc, u8 *out) +{ +	struct sha256_state *sctx = shash_desc_ctx(desc); +	__be32 *dst = (__be32 *)out; +	int i; + +	sha2_final(desc); + +	for (i = 0; i < SHA256_DIGEST_SIZE / sizeof(__be32); i++) +		put_unaligned_be32(sctx->state[i], dst++); + +	*sctx = (struct sha256_state){}; +	return 0; +} + +static void sha2_finup(struct shash_desc *desc, const u8 *data, +		       unsigned int len) +{ +	struct sha256_state *sctx = shash_desc_ctx(desc); +	int blocks; + +	if (sctx->count || !len || (len % SHA256_BLOCK_SIZE)) { +		sha2_update(desc, data, len); +		sha2_final(desc); +		return; +	} + +	/* +	 * Use a fast path if the input is a multiple of 64 bytes. In +	 * this case, there is no need to copy data around, and we can +	 * perform the entire digest calculation in a single invocation +	 * of sha2_ce_transform() +	 */ +	blocks = len / SHA256_BLOCK_SIZE; + +	kernel_neon_begin_partial(28); +	sha2_ce_transform(blocks, data, sctx->state, NULL, len); +	kernel_neon_end(); +	data += blocks * SHA256_BLOCK_SIZE; +} + +static int sha224_finup(struct shash_desc *desc, const u8 *data, +			unsigned int len, u8 *out) +{ +	struct sha256_state *sctx = shash_desc_ctx(desc); +	__be32 *dst = (__be32 *)out; +	int i; + +	sha2_finup(desc, data, len); + +	for (i = 0; i < SHA224_DIGEST_SIZE / sizeof(__be32); i++) +		put_unaligned_be32(sctx->state[i], dst++); + +	*sctx = (struct sha256_state){}; +	return 0; +} + +static int sha256_finup(struct shash_desc *desc, const u8 *data, +			unsigned int len, u8 *out) +{ +	struct sha256_state *sctx = shash_desc_ctx(desc); +	__be32 *dst = (__be32 *)out; +	int i; + +	sha2_finup(desc, data, len); + +	for (i = 0; i < SHA256_DIGEST_SIZE / sizeof(__be32); i++) +		put_unaligned_be32(sctx->state[i], dst++); + +	*sctx = (struct sha256_state){}; +	return 0; +} + +static int sha2_export(struct shash_desc *desc, void *out) +{ +	struct sha256_state *sctx = shash_desc_ctx(desc); +	struct sha256_state *dst = out; + +	*dst = *sctx; +	return 0; +} + +static int sha2_import(struct shash_desc *desc, const void *in) +{ +	struct sha256_state *sctx = shash_desc_ctx(desc); +	struct sha256_state const *src = in; + +	*sctx = *src; +	return 0; +} + +static struct shash_alg algs[] = { { +	.init			= sha224_init, +	.update			= sha2_update, +	.final			= sha224_final, +	.finup			= sha224_finup, +	.export			= sha2_export, +	.import			= sha2_import, +	.descsize		= sizeof(struct sha256_state), +	.digestsize		= SHA224_DIGEST_SIZE, +	.statesize		= sizeof(struct sha256_state), +	.base			= { +		.cra_name		= "sha224", +		.cra_driver_name	= "sha224-ce", +		.cra_priority		= 200, +		.cra_flags		= CRYPTO_ALG_TYPE_SHASH, +		.cra_blocksize		= SHA256_BLOCK_SIZE, +		.cra_module		= THIS_MODULE, +	} +}, { +	.init			= sha256_init, +	.update			= sha2_update, +	.final			= sha256_final, +	.finup			= sha256_finup, +	.export			= sha2_export, +	.import			= sha2_import, +	.descsize		= sizeof(struct sha256_state), +	.digestsize		= SHA256_DIGEST_SIZE, +	.statesize		= sizeof(struct sha256_state), +	.base			= { +		.cra_name		= "sha256", +		.cra_driver_name	= "sha256-ce", +		.cra_priority		= 200, +		.cra_flags		= CRYPTO_ALG_TYPE_SHASH, +		.cra_blocksize		= SHA256_BLOCK_SIZE, +		.cra_module		= THIS_MODULE, +	} +} }; + +static int __init sha2_ce_mod_init(void) +{ +	return crypto_register_shashes(algs, ARRAY_SIZE(algs)); +} + +static void __exit sha2_ce_mod_fini(void) +{ +	crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); +} + +module_cpu_feature_match(SHA2, sha2_ce_mod_init); +module_exit(sha2_ce_mod_fini); diff --git a/arch/arm64/include/asm/Kbuild b/arch/arm64/include/asm/Kbuild index 79a642d199f..0b3fcf86e6b 100644 --- a/arch/arm64/include/asm/Kbuild +++ b/arch/arm64/include/asm/Kbuild @@ -10,8 +10,10 @@ generic-y += delay.h  generic-y += div64.h  generic-y += dma.h  generic-y += emergency-restart.h +generic-y += early_ioremap.h  generic-y += errno.h  generic-y += ftrace.h +generic-y += hash.h  generic-y += hw_irq.h  generic-y += ioctl.h  generic-y += ioctls.h @@ -22,25 +24,27 @@ generic-y += kmap_types.h  generic-y += kvm_para.h  generic-y += local.h  generic-y += local64.h +generic-y += mcs_spinlock.h  generic-y += mman.h  generic-y += msgbuf.h  generic-y += mutex.h  generic-y += pci.h -generic-y += percpu.h  generic-y += poll.h -generic-y += posix_types.h +generic-y += preempt.h  generic-y += resource.h +generic-y += rwsem.h  generic-y += scatterlist.h  generic-y += sections.h  generic-y += segment.h  generic-y += sembuf.h  generic-y += serial.h  generic-y += shmbuf.h +generic-y += simd.h  generic-y += sizes.h  generic-y += socket.h  generic-y += sockios.h -generic-y += switch_to.h  generic-y += swab.h +generic-y += switch_to.h  generic-y += termbits.h  generic-y += termios.h  generic-y += topology.h diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h index c9f1d2816c2..9400596a0f3 100644 --- a/arch/arm64/include/asm/arch_timer.h +++ b/arch/arm64/include/asm/arch_timer.h @@ -92,19 +92,49 @@ static inline u32 arch_timer_get_cntfrq(void)  	return val;  } -static inline void arch_counter_set_user_access(void) +static inline u32 arch_timer_get_cntkctl(void)  {  	u32 cntkctl; - -	/* Disable user access to the timers and the physical counter. */  	asm volatile("mrs	%0, cntkctl_el1" : "=r" (cntkctl)); -	cntkctl &= ~((3 << 8) | (1 << 0)); +	return cntkctl; +} -	/* Enable user access to the virtual counter and frequency. */ -	cntkctl |= (1 << 1); +static inline void arch_timer_set_cntkctl(u32 cntkctl) +{  	asm volatile("msr	cntkctl_el1, %0" : : "r" (cntkctl));  } +static inline void arch_counter_set_user_access(void) +{ +	u32 cntkctl = arch_timer_get_cntkctl(); + +	/* Disable user access to the timers and the physical counter */ +	/* Also disable virtual event stream */ +	cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN +			| ARCH_TIMER_USR_VT_ACCESS_EN +			| ARCH_TIMER_VIRT_EVT_EN +			| ARCH_TIMER_USR_PCT_ACCESS_EN); + +	/* Enable user access to the virtual counter */ +	cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN; + +	arch_timer_set_cntkctl(cntkctl); +} + +static inline void arch_timer_evtstrm_enable(int divider) +{ +	u32 cntkctl = arch_timer_get_cntkctl(); +	cntkctl &= ~ARCH_TIMER_EVT_TRIGGER_MASK; +	/* Set the divider and enable virtual event stream */ +	cntkctl |= (divider << ARCH_TIMER_EVT_TRIGGER_SHIFT) +			| ARCH_TIMER_VIRT_EVT_EN; +	arch_timer_set_cntkctl(cntkctl); +	elf_hwcap |= HWCAP_EVTSTRM; +#ifdef CONFIG_COMPAT +	compat_elf_hwcap |= COMPAT_HWCAP_EVTSTRM; +#endif +} +  static inline u64 arch_counter_get_cntvct(void)  {  	u64 cval; diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h index 5aceb83b3f5..5901480bfdc 100644 --- a/arch/arm64/include/asm/assembler.h +++ b/arch/arm64/include/asm/assembler.h @@ -21,6 +21,7 @@  #endif  #include <asm/ptrace.h> +#include <asm/thread_info.h>  /*   * Stack pushing/popping (register pairs only). Equivalent to store decrement @@ -68,23 +69,31 @@  	msr	daifclr, #8  	.endm -	.macro	disable_step, tmp +	.macro	disable_step_tsk, flgs, tmp +	tbz	\flgs, #TIF_SINGLESTEP, 9990f  	mrs	\tmp, mdscr_el1  	bic	\tmp, \tmp, #1  	msr	mdscr_el1, \tmp +	isb	// Synchronise with enable_dbg +9990:  	.endm -	.macro	enable_step, tmp +	.macro	enable_step_tsk, flgs, tmp +	tbz	\flgs, #TIF_SINGLESTEP, 9990f +	disable_dbg  	mrs	\tmp, mdscr_el1  	orr	\tmp, \tmp, #1  	msr	mdscr_el1, \tmp +9990:  	.endm -	.macro	enable_dbg_if_not_stepping, tmp -	mrs	\tmp, mdscr_el1 -	tbnz	\tmp, #0, 9990f -	enable_dbg -9990: +/* + * Enable both debug exceptions and interrupts. This is likely to be + * faster than two daifclr operations, since writes to this register + * are self-synchronising. + */ +	.macro	enable_dbg_and_irq +	msr	daifclr, #(8 | 2)  	.endm  /* @@ -115,3 +124,34 @@ lr	.req	x30		// link register  	.align	7  	b	\label  	.endm + +/* + * Select code when configured for BE. + */ +#ifdef CONFIG_CPU_BIG_ENDIAN +#define CPU_BE(code...) code +#else +#define CPU_BE(code...) +#endif + +/* + * Select code when configured for LE. + */ +#ifdef CONFIG_CPU_BIG_ENDIAN +#define CPU_LE(code...) +#else +#define CPU_LE(code...) code +#endif + +/* + * Define a macro that constructs a 64-bit value by concatenating two + * 32-bit registers. Note that on big endian systems the order of the + * registers is swapped. + */ +#ifndef CONFIG_CPU_BIG_ENDIAN +	.macro	regs_to_64, rd, lbits, hbits +#else +	.macro	regs_to_64, rd, hbits, lbits +#endif +	orr	\rd, \lbits, \hbits, lsl #32 +	.endm diff --git a/arch/arm64/include/asm/atomic.h b/arch/arm64/include/asm/atomic.h index 83636446857..65f1569ac96 100644 --- a/arch/arm64/include/asm/atomic.h +++ b/arch/arm64/include/asm/atomic.h @@ -54,8 +54,7 @@ static inline void atomic_add(int i, atomic_t *v)  "	stxr	%w1, %w0, %2\n"  "	cbnz	%w1, 1b"  	: "=&r" (result), "=&r" (tmp), "+Q" (v->counter) -	: "Ir" (i) -	: "cc"); +	: "Ir" (i));  }  static inline int atomic_add_return(int i, atomic_t *v) @@ -64,14 +63,15 @@ static inline int atomic_add_return(int i, atomic_t *v)  	int result;  	asm volatile("// atomic_add_return\n" -"1:	ldaxr	%w0, %2\n" +"1:	ldxr	%w0, %2\n"  "	add	%w0, %w0, %w3\n"  "	stlxr	%w1, %w0, %2\n"  "	cbnz	%w1, 1b"  	: "=&r" (result), "=&r" (tmp), "+Q" (v->counter)  	: "Ir" (i) -	: "cc", "memory"); +	: "memory"); +	smp_mb();  	return result;  } @@ -86,8 +86,7 @@ static inline void atomic_sub(int i, atomic_t *v)  "	stxr	%w1, %w0, %2\n"  "	cbnz	%w1, 1b"  	: "=&r" (result), "=&r" (tmp), "+Q" (v->counter) -	: "Ir" (i) -	: "cc"); +	: "Ir" (i));  }  static inline int atomic_sub_return(int i, atomic_t *v) @@ -96,14 +95,15 @@ static inline int atomic_sub_return(int i, atomic_t *v)  	int result;  	asm volatile("// atomic_sub_return\n" -"1:	ldaxr	%w0, %2\n" +"1:	ldxr	%w0, %2\n"  "	sub	%w0, %w0, %w3\n"  "	stlxr	%w1, %w0, %2\n"  "	cbnz	%w1, 1b"  	: "=&r" (result), "=&r" (tmp), "+Q" (v->counter)  	: "Ir" (i) -	: "cc", "memory"); +	: "memory"); +	smp_mb();  	return result;  } @@ -112,34 +112,23 @@ static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new)  	unsigned long tmp;  	int oldval; +	smp_mb(); +  	asm volatile("// atomic_cmpxchg\n" -"1:	ldaxr	%w1, %2\n" +"1:	ldxr	%w1, %2\n"  "	cmp	%w1, %w3\n"  "	b.ne	2f\n" -"	stlxr	%w0, %w4, %2\n" +"	stxr	%w0, %w4, %2\n"  "	cbnz	%w0, 1b\n"  "2:"  	: "=&r" (tmp), "=&r" (oldval), "+Q" (ptr->counter)  	: "Ir" (old), "r" (new) -	: "cc", "memory"); +	: "cc"); +	smp_mb();  	return oldval;  } -static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr) -{ -	unsigned long tmp, tmp2; - -	asm volatile("// atomic_clear_mask\n" -"1:	ldxr	%0, %2\n" -"	bic	%0, %0, %3\n" -"	stxr	%w1, %0, %2\n" -"	cbnz	%w1, 1b" -	: "=&r" (tmp), "=&r" (tmp2), "+Q" (*addr) -	: "Ir" (mask) -	: "cc"); -} -  #define atomic_xchg(v, new) (xchg(&((v)->counter), new))  static inline int __atomic_add_unless(atomic_t *v, int a, int u) @@ -163,17 +152,12 @@ static inline int __atomic_add_unless(atomic_t *v, int a, int u)  #define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0) -#define smp_mb__before_atomic_dec()	smp_mb() -#define smp_mb__after_atomic_dec()	smp_mb() -#define smp_mb__before_atomic_inc()	smp_mb() -#define smp_mb__after_atomic_inc()	smp_mb() -  /*   * 64-bit atomic operations.   */  #define ATOMIC64_INIT(i) { (i) } -#define atomic64_read(v)	(*(volatile long long *)&(v)->counter) +#define atomic64_read(v)	(*(volatile long *)&(v)->counter)  #define atomic64_set(v,i)	(((v)->counter) = (i))  static inline void atomic64_add(u64 i, atomic64_t *v) @@ -187,8 +171,7 @@ static inline void atomic64_add(u64 i, atomic64_t *v)  "	stxr	%w1, %0, %2\n"  "	cbnz	%w1, 1b"  	: "=&r" (result), "=&r" (tmp), "+Q" (v->counter) -	: "Ir" (i) -	: "cc"); +	: "Ir" (i));  }  static inline long atomic64_add_return(long i, atomic64_t *v) @@ -197,14 +180,15 @@ static inline long atomic64_add_return(long i, atomic64_t *v)  	unsigned long tmp;  	asm volatile("// atomic64_add_return\n" -"1:	ldaxr	%0, %2\n" +"1:	ldxr	%0, %2\n"  "	add	%0, %0, %3\n"  "	stlxr	%w1, %0, %2\n"  "	cbnz	%w1, 1b"  	: "=&r" (result), "=&r" (tmp), "+Q" (v->counter)  	: "Ir" (i) -	: "cc", "memory"); +	: "memory"); +	smp_mb();  	return result;  } @@ -219,8 +203,7 @@ static inline void atomic64_sub(u64 i, atomic64_t *v)  "	stxr	%w1, %0, %2\n"  "	cbnz	%w1, 1b"  	: "=&r" (result), "=&r" (tmp), "+Q" (v->counter) -	: "Ir" (i) -	: "cc"); +	: "Ir" (i));  }  static inline long atomic64_sub_return(long i, atomic64_t *v) @@ -229,14 +212,15 @@ static inline long atomic64_sub_return(long i, atomic64_t *v)  	unsigned long tmp;  	asm volatile("// atomic64_sub_return\n" -"1:	ldaxr	%0, %2\n" +"1:	ldxr	%0, %2\n"  "	sub	%0, %0, %3\n"  "	stlxr	%w1, %0, %2\n"  "	cbnz	%w1, 1b"  	: "=&r" (result), "=&r" (tmp), "+Q" (v->counter)  	: "Ir" (i) -	: "cc", "memory"); +	: "memory"); +	smp_mb();  	return result;  } @@ -245,17 +229,20 @@ static inline long atomic64_cmpxchg(atomic64_t *ptr, long old, long new)  	long oldval;  	unsigned long res; +	smp_mb(); +  	asm volatile("// atomic64_cmpxchg\n" -"1:	ldaxr	%1, %2\n" +"1:	ldxr	%1, %2\n"  "	cmp	%1, %3\n"  "	b.ne	2f\n" -"	stlxr	%w0, %4, %2\n" +"	stxr	%w0, %4, %2\n"  "	cbnz	%w0, 1b\n"  "2:"  	: "=&r" (res), "=&r" (oldval), "+Q" (ptr->counter)  	: "Ir" (old), "r" (new) -	: "cc", "memory"); +	: "cc"); +	smp_mb();  	return oldval;  } @@ -267,11 +254,12 @@ static inline long atomic64_dec_if_positive(atomic64_t *v)  	unsigned long tmp;  	asm volatile("// atomic64_dec_if_positive\n" -"1:	ldaxr	%0, %2\n" +"1:	ldxr	%0, %2\n"  "	subs	%0, %0, #1\n"  "	b.mi	2f\n"  "	stlxr	%w1, %0, %2\n"  "	cbnz	%w1, 1b\n" +"	dmb	ish\n"  "2:"  	: "=&r" (result), "=&r" (tmp), "+Q" (v->counter)  	: diff --git a/arch/arm64/include/asm/barrier.h b/arch/arm64/include/asm/barrier.h index d4a63338a53..6389d60574d 100644 --- a/arch/arm64/include/asm/barrier.h +++ b/arch/arm64/include/asm/barrier.h @@ -25,20 +25,71 @@  #define wfi()		asm volatile("wfi" : : : "memory")  #define isb()		asm volatile("isb" : : : "memory") -#define dsb()		asm volatile("dsb sy" : : : "memory") +#define dmb(opt)	asm volatile("dmb " #opt : : : "memory") +#define dsb(opt)	asm volatile("dsb " #opt : : : "memory") -#define mb()		dsb() -#define rmb()		asm volatile("dsb ld" : : : "memory") -#define wmb()		asm volatile("dsb st" : : : "memory") +#define mb()		dsb(sy) +#define rmb()		dsb(ld) +#define wmb()		dsb(st)  #ifndef CONFIG_SMP  #define smp_mb()	barrier()  #define smp_rmb()	barrier()  #define smp_wmb()	barrier() + +#define smp_store_release(p, v)						\ +do {									\ +	compiletime_assert_atomic_type(*p);				\ +	barrier();							\ +	ACCESS_ONCE(*p) = (v);						\ +} while (0) + +#define smp_load_acquire(p)						\ +({									\ +	typeof(*p) ___p1 = ACCESS_ONCE(*p);				\ +	compiletime_assert_atomic_type(*p);				\ +	barrier();							\ +	___p1;								\ +}) +  #else -#define smp_mb()	asm volatile("dmb ish" : : : "memory") -#define smp_rmb()	asm volatile("dmb ishld" : : : "memory") -#define smp_wmb()	asm volatile("dmb ishst" : : : "memory") + +#define smp_mb()	dmb(ish) +#define smp_rmb()	dmb(ishld) +#define smp_wmb()	dmb(ishst) + +#define smp_store_release(p, v)						\ +do {									\ +	compiletime_assert_atomic_type(*p);				\ +	switch (sizeof(*p)) {						\ +	case 4:								\ +		asm volatile ("stlr %w1, %0"				\ +				: "=Q" (*p) : "r" (v) : "memory");	\ +		break;							\ +	case 8:								\ +		asm volatile ("stlr %1, %0"				\ +				: "=Q" (*p) : "r" (v) : "memory");	\ +		break;							\ +	}								\ +} while (0) + +#define smp_load_acquire(p)						\ +({									\ +	typeof(*p) ___p1;						\ +	compiletime_assert_atomic_type(*p);				\ +	switch (sizeof(*p)) {						\ +	case 4:								\ +		asm volatile ("ldar %w0, %1"				\ +			: "=r" (___p1) : "Q" (*p) : "memory");		\ +		break;							\ +	case 8:								\ +		asm volatile ("ldar %0, %1"				\ +			: "=r" (___p1) : "Q" (*p) : "memory");		\ +		break;							\ +	}								\ +	___p1;								\ +}) +  #endif  #define read_barrier_depends()		do { } while(0) @@ -47,6 +98,9 @@  #define set_mb(var, value)	do { var = value; smp_mb(); } while (0)  #define nop()		asm volatile("nop"); +#define smp_mb__before_atomic()	smp_mb() +#define smp_mb__after_atomic()	smp_mb() +  #endif	/* __ASSEMBLY__ */  #endif	/* __ASM_BARRIER_H */ diff --git a/arch/arm64/include/asm/bitops.h b/arch/arm64/include/asm/bitops.h index aa5b59d6ba4..9c19594ce7c 100644 --- a/arch/arm64/include/asm/bitops.h +++ b/arch/arm64/include/asm/bitops.h @@ -17,17 +17,8 @@  #define __ASM_BITOPS_H  #include <linux/compiler.h> -  #include <asm/barrier.h> -/* - * clear_bit may not imply a memory barrier - */ -#ifndef smp_mb__before_clear_bit -#define smp_mb__before_clear_bit()	smp_mb() -#define smp_mb__after_clear_bit()	smp_mb() -#endif -  #ifndef _LINUX_BITOPS_H  #error only <linux/bitops.h> can be included directly  #endif diff --git a/arch/arm64/include/asm/cache.h b/arch/arm64/include/asm/cache.h index 390308a67f0..88cc05b5f3a 100644 --- a/arch/arm64/include/asm/cache.h +++ b/arch/arm64/include/asm/cache.h @@ -16,6 +16,8 @@  #ifndef __ASM_CACHE_H  #define __ASM_CACHE_H +#include <asm/cachetype.h> +  #define L1_CACHE_SHIFT		6  #define L1_CACHE_BYTES		(1 << L1_CACHE_SHIFT) @@ -27,6 +29,15 @@   * the CPU.   */  #define ARCH_DMA_MINALIGN	L1_CACHE_BYTES -#define ARCH_SLAB_MINALIGN	8 + +#ifndef __ASSEMBLY__ + +static inline int cache_line_size(void) +{ +	u32 cwg = cache_type_cwg(); +	return cwg ? 4 << cwg : L1_CACHE_BYTES; +} + +#endif	/* __ASSEMBLY__ */  #endif diff --git a/arch/arm64/include/asm/cacheflush.h b/arch/arm64/include/asm/cacheflush.h index fea9ee32720..a5176cf32da 100644 --- a/arch/arm64/include/asm/cacheflush.h +++ b/arch/arm64/include/asm/cacheflush.h @@ -85,6 +85,13 @@ static inline void flush_cache_page(struct vm_area_struct *vma,  }  /* + * Cache maintenance functions used by the DMA API. No to be used directly. + */ +extern void __dma_map_area(const void *, size_t, int); +extern void __dma_unmap_area(const void *, size_t, int); +extern void __dma_flush_range(const void *, const void *); + +/*   * Copy user data from/to a page which is mapped into a different   * processes address space.  Really, we want to allow our "user   * space" model to handle this. @@ -116,6 +123,7 @@ extern void flush_dcache_page(struct page *);  static inline void __flush_icache_all(void)  {  	asm("ic	ialluis"); +	dsb(ish);  }  #define flush_dcache_mmap_lock(mapping) \ @@ -142,7 +150,7 @@ static inline void flush_cache_vmap(unsigned long start, unsigned long end)  	 * set_pte_at() called from vmap_pte_range() does not  	 * have a DSB after cleaning the cache line.  	 */ -	dsb(); +	dsb(ish);  }  static inline void flush_cache_vunmap(unsigned long start, unsigned long end) diff --git a/arch/arm64/include/asm/cachetype.h b/arch/arm64/include/asm/cachetype.h index 85f5f511352..4b23e758d5e 100644 --- a/arch/arm64/include/asm/cachetype.h +++ b/arch/arm64/include/asm/cachetype.h @@ -20,12 +20,16 @@  #define CTR_L1IP_SHIFT		14  #define CTR_L1IP_MASK		3 +#define CTR_CWG_SHIFT		24 +#define CTR_CWG_MASK		15  #define ICACHE_POLICY_RESERVED	0  #define ICACHE_POLICY_AIVIVT	1  #define ICACHE_POLICY_VIPT	2  #define ICACHE_POLICY_PIPT	3 +#ifndef __ASSEMBLY__ +  static inline u32 icache_policy(void)  {  	return (read_cpuid_cachetype() >> CTR_L1IP_SHIFT) & CTR_L1IP_MASK; @@ -45,4 +49,11 @@ static inline int icache_is_aivivt(void)  	return icache_policy() == ICACHE_POLICY_AIVIVT;  } +static inline u32 cache_type_cwg(void) +{ +	return (read_cpuid_cachetype() >> CTR_CWG_SHIFT) & CTR_CWG_MASK; +} + +#endif	/* __ASSEMBLY__ */ +  #endif	/* __ASM_CACHETYPE_H */ diff --git a/arch/arm64/include/asm/cmpxchg.h b/arch/arm64/include/asm/cmpxchg.h index 8a8ce0e73a3..ddb9d783055 100644 --- a/arch/arm64/include/asm/cmpxchg.h +++ b/arch/arm64/include/asm/cmpxchg.h @@ -29,49 +29,55 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size  	switch (size) {  	case 1:  		asm volatile("//	__xchg1\n" -		"1:	ldaxrb	%w0, %2\n" +		"1:	ldxrb	%w0, %2\n"  		"	stlxrb	%w1, %w3, %2\n"  		"	cbnz	%w1, 1b\n"  			: "=&r" (ret), "=&r" (tmp), "+Q" (*(u8 *)ptr)  			: "r" (x) -			: "cc", "memory"); +			: "memory");  		break;  	case 2:  		asm volatile("//	__xchg2\n" -		"1:	ldaxrh	%w0, %2\n" +		"1:	ldxrh	%w0, %2\n"  		"	stlxrh	%w1, %w3, %2\n"  		"	cbnz	%w1, 1b\n"  			: "=&r" (ret), "=&r" (tmp), "+Q" (*(u16 *)ptr)  			: "r" (x) -			: "cc", "memory"); +			: "memory");  		break;  	case 4:  		asm volatile("//	__xchg4\n" -		"1:	ldaxr	%w0, %2\n" +		"1:	ldxr	%w0, %2\n"  		"	stlxr	%w1, %w3, %2\n"  		"	cbnz	%w1, 1b\n"  			: "=&r" (ret), "=&r" (tmp), "+Q" (*(u32 *)ptr)  			: "r" (x) -			: "cc", "memory"); +			: "memory");  		break;  	case 8:  		asm volatile("//	__xchg8\n" -		"1:	ldaxr	%0, %2\n" +		"1:	ldxr	%0, %2\n"  		"	stlxr	%w1, %3, %2\n"  		"	cbnz	%w1, 1b\n"  			: "=&r" (ret), "=&r" (tmp), "+Q" (*(u64 *)ptr)  			: "r" (x) -			: "cc", "memory"); +			: "memory");  		break;  	default:  		BUILD_BUG();  	} +	smp_mb();  	return ret;  }  #define xchg(ptr,x) \ -	((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr)))) +({ \ +	__typeof__(*(ptr)) __ret; \ +	__ret = (__typeof__(*(ptr))) \ +		__xchg((unsigned long)(x), (ptr), sizeof(*(ptr))); \ +	__ret; \ +})  static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,  				      unsigned long new, int size) @@ -158,19 +164,27 @@ static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old,  	return ret;  } -#define cmpxchg(ptr,o,n)						\ -	((__typeof__(*(ptr)))__cmpxchg_mb((ptr),			\ -					  (unsigned long)(o),		\ -					  (unsigned long)(n),		\ -					  sizeof(*(ptr)))) - -#define cmpxchg_local(ptr,o,n)						\ -	((__typeof__(*(ptr)))__cmpxchg((ptr),				\ -				       (unsigned long)(o),		\ -				       (unsigned long)(n),		\ -				       sizeof(*(ptr)))) +#define cmpxchg(ptr, o, n) \ +({ \ +	__typeof__(*(ptr)) __ret; \ +	__ret = (__typeof__(*(ptr))) \ +		__cmpxchg_mb((ptr), (unsigned long)(o), (unsigned long)(n), \ +			     sizeof(*(ptr))); \ +	__ret; \ +}) + +#define cmpxchg_local(ptr, o, n) \ +({ \ +	__typeof__(*(ptr)) __ret; \ +	__ret = (__typeof__(*(ptr))) \ +		__cmpxchg((ptr), (unsigned long)(o), \ +			  (unsigned long)(n), sizeof(*(ptr))); \ +	__ret; \ +})  #define cmpxchg64(ptr,o,n)		cmpxchg((ptr),(o),(n))  #define cmpxchg64_local(ptr,o,n)	cmpxchg_local((ptr),(o),(n)) +#define cmpxchg64_relaxed(ptr,o,n)	cmpxchg_local((ptr),(o),(n)) +  #endif	/* __ASM_CMPXCHG_H */ diff --git a/arch/arm64/include/asm/compat.h b/arch/arm64/include/asm/compat.h index 899af807ef0..253e33bc94f 100644 --- a/arch/arm64/include/asm/compat.h +++ b/arch/arm64/include/asm/compat.h @@ -26,7 +26,11 @@  #include <linux/ptrace.h>  #define COMPAT_USER_HZ		100 +#ifdef __AARCH64EB__ +#define COMPAT_UTS_MACHINE	"armv8b\0\0" +#else  #define COMPAT_UTS_MACHINE	"armv8l\0\0" +#endif  typedef u32		compat_size_t;  typedef s32		compat_ssize_t; @@ -73,13 +77,23 @@ struct compat_timeval {  };  struct compat_stat { +#ifdef __AARCH64EB__ +	short		st_dev; +	short		__pad1; +#else  	compat_dev_t	st_dev; +#endif  	compat_ino_t	st_ino;  	compat_mode_t	st_mode;  	compat_ushort_t	st_nlink;  	__compat_uid16_t	st_uid;  	__compat_gid16_t	st_gid; +#ifdef __AARCH64EB__ +	short		st_rdev; +	short		__pad2; +#else  	compat_dev_t	st_rdev; +#endif  	compat_off_t	st_size;  	compat_off_t	st_blksize;  	compat_off_t	st_blocks; @@ -214,7 +228,7 @@ static inline compat_uptr_t ptr_to_compat(void __user *uptr)  	return (u32)(unsigned long)uptr;  } -#define compat_user_stack_pointer() (current_pt_regs()->compat_sp) +#define compat_user_stack_pointer() (user_stack_pointer(current_pt_regs()))  static inline void __user *arch_compat_alloc_user_space(long len)  { @@ -291,11 +305,6 @@ static inline int is_compat_thread(struct thread_info *thread)  #else /* !CONFIG_COMPAT */ -static inline int is_compat_task(void) -{ -	return 0; -} -  static inline int is_compat_thread(struct thread_info *thread)  {  	return 0; diff --git a/arch/arm64/include/asm/cpu_ops.h b/arch/arm64/include/asm/cpu_ops.h new file mode 100644 index 00000000000..d7b4b38a8e8 --- /dev/null +++ b/arch/arm64/include/asm/cpu_ops.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2013 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_OPS_H +#define __ASM_CPU_OPS_H + +#include <linux/init.h> +#include <linux/threads.h> + +struct device_node; + +/** + * struct cpu_operations - Callback operations for hotplugging CPUs. + * + * @name:	Name of the property as appears in a devicetree cpu node's + *		enable-method property. + * @cpu_init:	Reads any data necessary for a specific enable-method from the + *		devicetree, for a given cpu node and proposed logical id. + * @cpu_prepare: Early one-time preparation step for a cpu. If there is a + *		mechanism for doing so, tests whether it is possible to boot + *		the given CPU. + * @cpu_boot:	Boots a cpu into the kernel. + * @cpu_postboot: Optionally, perform any post-boot cleanup or necesary + *		synchronisation. Called from the cpu being booted. + * @cpu_disable: Prepares a cpu to die. May fail for some mechanism-specific + * 		reason, which will cause the hot unplug to be aborted. Called + * 		from the cpu to be killed. + * @cpu_die:	Makes a cpu leave the kernel. Must not fail. Called from the + *		cpu being killed. + * @cpu_kill:  Ensures a cpu has left the kernel. Called from another cpu. + * @cpu_suspend: Suspends a cpu and saves the required context. May fail owing + *               to wrong parameters or error conditions. Called from the + *               CPU being suspended. Must be called with IRQs disabled. + */ +struct cpu_operations { +	const char	*name; +	int		(*cpu_init)(struct device_node *, unsigned int); +	int		(*cpu_prepare)(unsigned int); +	int		(*cpu_boot)(unsigned int); +	void		(*cpu_postboot)(void); +#ifdef CONFIG_HOTPLUG_CPU +	int		(*cpu_disable)(unsigned int cpu); +	void		(*cpu_die)(unsigned int cpu); +	int		(*cpu_kill)(unsigned int cpu); +#endif +#ifdef CONFIG_ARM64_CPU_SUSPEND +	int		(*cpu_suspend)(unsigned long); +#endif +}; + +extern const struct cpu_operations *cpu_ops[NR_CPUS]; +extern int __init cpu_read_ops(struct device_node *dn, int cpu); +extern void __init cpu_read_bootcpu_ops(void); + +#endif /* ifndef __ASM_CPU_OPS_H */ diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h new file mode 100644 index 00000000000..cd4ac051648 --- /dev/null +++ b/arch/arm64/include/asm/cpufeature.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2014 Linaro Ltd. <ard.biesheuvel@linaro.org> + * + * 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_CPUFEATURE_H +#define __ASM_CPUFEATURE_H + +#include <asm/hwcap.h> + +/* + * In the arm64 world (as in the ARM world), elf_hwcap is used both internally + * in the kernel and for user space to keep track of which optional features + * are supported by the current system. So let's map feature 'x' to HWCAP_x. + * Note that HWCAP_x constants are bit fields so we need to take the log. + */ + +#define MAX_CPU_FEATURES	(8 * sizeof(elf_hwcap)) +#define cpu_feature(x)		ilog2(HWCAP_ ## x) + +static inline bool cpu_have_feature(unsigned int num) +{ +	return elf_hwcap & (1UL << num); +} + +#endif diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h index 5fe138e0b82..27f54a7cc81 100644 --- a/arch/arm64/include/asm/cputype.h +++ b/arch/arm64/include/asm/cputype.h @@ -16,23 +16,23 @@  #ifndef __ASM_CPUTYPE_H  #define __ASM_CPUTYPE_H -#define ID_MIDR_EL1		"midr_el1" -#define ID_MPIDR_EL1		"mpidr_el1" -#define ID_CTR_EL0		"ctr_el0" - -#define ID_AA64PFR0_EL1		"id_aa64pfr0_el1" -#define ID_AA64DFR0_EL1		"id_aa64dfr0_el1" -#define ID_AA64AFR0_EL1		"id_aa64afr0_el1" -#define ID_AA64ISAR0_EL1	"id_aa64isar0_el1" -#define ID_AA64MMFR0_EL1	"id_aa64mmfr0_el1" -  #define INVALID_HWID		ULONG_MAX  #define MPIDR_HWID_BITMASK	0xff00ffffff +#define MPIDR_LEVEL_BITS_SHIFT	3 +#define MPIDR_LEVEL_BITS	(1 << MPIDR_LEVEL_BITS_SHIFT) +#define MPIDR_LEVEL_MASK	((1 << MPIDR_LEVEL_BITS) - 1) + +#define MPIDR_LEVEL_SHIFT(level) \ +	(((1 << level) >> 1) << MPIDR_LEVEL_BITS_SHIFT) + +#define MPIDR_AFFINITY_LEVEL(mpidr, level) \ +	((mpidr >> MPIDR_LEVEL_SHIFT(level)) & MPIDR_LEVEL_MASK) +  #define read_cpuid(reg) ({						\  	u64 __val;							\ -	asm("mrs	%0, " reg : "=r" (__val));			\ +	asm("mrs	%0, " #reg : "=r" (__val));			\  	__val;								\  }) @@ -41,6 +41,7 @@  #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 APM_CPU_PART_POTENZA	0x0000 @@ -54,12 +55,12 @@   */  static inline u32 __attribute_const__ read_cpuid_id(void)  { -	return read_cpuid(ID_MIDR_EL1); +	return read_cpuid(MIDR_EL1);  }  static inline u64 __attribute_const__ read_cpuid_mpidr(void)  { -	return read_cpuid(ID_MPIDR_EL1); +	return read_cpuid(MPIDR_EL1);  }  static inline unsigned int __attribute_const__ read_cpuid_implementor(void) @@ -74,7 +75,7 @@ static inline unsigned int __attribute_const__ read_cpuid_part_number(void)  static inline u32 __attribute_const__ read_cpuid_cachetype(void)  { -	return read_cpuid(ID_CTR_EL0); +	return read_cpuid(CTR_EL0);  }  #endif /* __ASSEMBLY__ */ diff --git a/arch/arm64/include/asm/debug-monitors.h b/arch/arm64/include/asm/debug-monitors.h index a2232d07be9..6e9b5b36921 100644 --- a/arch/arm64/include/asm/debug-monitors.h +++ b/arch/arm64/include/asm/debug-monitors.h @@ -26,6 +26,53 @@  #define DBG_ESR_EVT_HWWP	0x2  #define DBG_ESR_EVT_BRK		0x6 +/* + * Break point instruction encoding + */ +#define BREAK_INSTR_SIZE		4 + +/* + * ESR values expected for dynamic and compile time BRK instruction + */ +#define DBG_ESR_VAL_BRK(x)	(0xf2000000 | ((x) & 0xfffff)) + +/* + * #imm16 values used for BRK instruction generation + * Allowed values for kgbd are 0x400 - 0x7ff + * 0x400: for dynamic BRK instruction + * 0x401: for compile time BRK instruction + */ +#define KGDB_DYN_DGB_BRK_IMM		0x400 +#define KDBG_COMPILED_DBG_BRK_IMM	0x401 + +/* + * BRK instruction encoding + * The #imm16 value should be placed at bits[20:5] within BRK ins + */ +#define AARCH64_BREAK_MON	0xd4200000 + +/* + * Extract byte from BRK instruction + */ +#define KGDB_DYN_DGB_BRK_INS_BYTE(x) \ +	((((AARCH64_BREAK_MON) & 0xffe0001f) >> (x * 8)) & 0xff) + +/* + * Extract byte from BRK #imm16 + */ +#define KGBD_DYN_DGB_BRK_IMM_BYTE(x) \ +	(((((KGDB_DYN_DGB_BRK_IMM) & 0xffff) << 5) >> (x * 8)) & 0xff) + +#define KGDB_DYN_DGB_BRK_BYTE(x) \ +	(KGDB_DYN_DGB_BRK_INS_BYTE(x) | KGBD_DYN_DGB_BRK_IMM_BYTE(x)) + +#define  KGDB_DYN_BRK_INS_BYTE0  KGDB_DYN_DGB_BRK_BYTE(0) +#define  KGDB_DYN_BRK_INS_BYTE1  KGDB_DYN_DGB_BRK_BYTE(1) +#define  KGDB_DYN_BRK_INS_BYTE2  KGDB_DYN_DGB_BRK_BYTE(2) +#define  KGDB_DYN_BRK_INS_BYTE3  KGDB_DYN_DGB_BRK_BYTE(3) + +#define CACHE_FLUSH_IS_SAFE		1 +  enum debug_el {  	DBG_ACTIVE_EL0 = 0,  	DBG_ACTIVE_EL1, @@ -43,25 +90,29 @@ enum debug_el {  #ifndef __ASSEMBLY__  struct task_struct; -#define local_dbg_save(flags)							\ -	do {									\ -		typecheck(unsigned long, flags);				\ -		asm volatile(							\ -		"mrs	%0, daif			// local_dbg_save\n"	\ -		"msr	daifset, #8"						\ -		: "=r" (flags) : : "memory");					\ -	} while (0) - -#define local_dbg_restore(flags)						\ -	do {									\ -		typecheck(unsigned long, flags);				\ -		asm volatile(							\ -		"msr	daif, %0			// local_dbg_restore\n"	\ -		: : "r" (flags) : "memory");					\ -	} while (0) -  #define DBG_ARCH_ID_RESERVED	0	/* In case of ptrace ABI updates. */ +#define DBG_HOOK_HANDLED	0 +#define DBG_HOOK_ERROR		1 + +struct step_hook { +	struct list_head node; +	int (*fn)(struct pt_regs *regs, unsigned int esr); +}; + +void register_step_hook(struct step_hook *hook); +void unregister_step_hook(struct step_hook *hook); + +struct break_hook { +	struct list_head node; +	u32 esr_val; +	u32 esr_mask; +	int (*fn)(struct pt_regs *regs, unsigned int esr); +}; + +void register_break_hook(struct break_hook *hook); +void unregister_break_hook(struct break_hook *hook); +  u8 debug_monitors_arch(void);  void enable_debug_monitors(enum debug_el el); diff --git a/arch/arm64/include/asm/dma-contiguous.h b/arch/arm64/include/asm/dma-contiguous.h new file mode 100644 index 00000000000..14c4c0ca7f2 --- /dev/null +++ b/arch/arm64/include/asm/dma-contiguous.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2013, The Linux Foundation. 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 and + * only 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. + */ + +#ifndef _ASM_DMA_CONTIGUOUS_H +#define _ASM_DMA_CONTIGUOUS_H + +#ifdef __KERNEL__ +#ifdef CONFIG_DMA_CMA + +#include <linux/types.h> + +static inline void +dma_contiguous_early_fixup(phys_addr_t base, unsigned long size) { } + +#endif +#endif + +#endif diff --git a/arch/arm64/include/asm/dma-mapping.h b/arch/arm64/include/asm/dma-mapping.h index 8d1810001ae..dc82e52acdb 100644 --- a/arch/arm64/include/asm/dma-mapping.h +++ b/arch/arm64/include/asm/dma-mapping.h @@ -23,11 +23,15 @@  #include <asm-generic/dma-coherent.h> -#define ARCH_HAS_DMA_GET_REQUIRED_MASK +#include <xen/xen.h> +#include <asm/xen/hypervisor.h> +#define DMA_ERROR_CODE	(~(dma_addr_t)0)  extern struct dma_map_ops *dma_ops; +extern struct dma_map_ops coherent_swiotlb_dma_ops; +extern struct dma_map_ops noncoherent_swiotlb_dma_ops; -static inline struct dma_map_ops *get_dma_ops(struct device *dev) +static inline struct dma_map_ops *__generic_dma_ops(struct device *dev)  {  	if (unlikely(!dev) || !dev->archdata.dma_ops)  		return dma_ops; @@ -35,6 +39,19 @@ static inline struct dma_map_ops *get_dma_ops(struct device *dev)  		return dev->archdata.dma_ops;  } +static inline struct dma_map_ops *get_dma_ops(struct device *dev) +{ +	if (xen_initial_domain()) +		return xen_dma_ops; +	else +		return __generic_dma_ops(dev); +} + +static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops) +{ +	dev->archdata.dma_ops = ops; +} +  #include <asm-generic/dma-mapping-common.h>  static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr) diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h new file mode 100644 index 00000000000..5a46c4e7f53 --- /dev/null +++ b/arch/arm64/include/asm/efi.h @@ -0,0 +1,14 @@ +#ifndef _ASM_EFI_H +#define _ASM_EFI_H + +#include <asm/io.h> + +#ifdef CONFIG_EFI +extern void efi_init(void); +extern void efi_idmap_init(void); +#else +#define efi_init() +#define efi_idmap_init() +#endif + +#endif /* _ASM_EFI_H */ diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h index e7fa87f9201..01d3aab64b7 100644 --- a/arch/arm64/include/asm/elf.h +++ b/arch/arm64/include/asm/elf.h @@ -90,11 +90,24 @@ typedef struct user_fpsimd_state elf_fpregset_t;   * These are used to set parameters in the core dumps.   */  #define ELF_CLASS	ELFCLASS64 +#ifdef __AARCH64EB__ +#define ELF_DATA	ELFDATA2MSB +#else  #define ELF_DATA	ELFDATA2LSB +#endif  #define ELF_ARCH	EM_AARCH64 +/* + * This yields a string that ld.so will use to load implementation + * specific libraries for optimization.  This is more specific in + * intent than poking at uname or /proc/cpuinfo. + */  #define ELF_PLATFORM_SIZE	16 +#ifdef __AARCH64EB__ +#define ELF_PLATFORM		("aarch64_be") +#else  #define ELF_PLATFORM		("aarch64") +#endif  /*   * This is used to ensure we don't load something for the wrong architecture. @@ -149,7 +162,12 @@ extern unsigned long arch_randomize_brk(struct mm_struct *mm);  #define arch_randomize_brk arch_randomize_brk  #ifdef CONFIG_COMPAT + +#ifdef __AARCH64EB__ +#define COMPAT_ELF_PLATFORM		("v8b") +#else  #define COMPAT_ELF_PLATFORM		("v8l") +#endif  #define COMPAT_ELF_ET_DYN_BASE		(randomize_et_dyn(2 * TASK_SIZE_32 / 3)) diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h index 78834123a32..72674f4c387 100644 --- a/arch/arm64/include/asm/esr.h +++ b/arch/arm64/include/asm/esr.h @@ -18,9 +18,11 @@  #ifndef __ASM_ESR_H  #define __ASM_ESR_H -#define ESR_EL1_EC_SHIFT	(26) -#define ESR_EL1_IL		(1U << 25) +#define ESR_EL1_WRITE		(1 << 6) +#define ESR_EL1_CM		(1 << 8) +#define ESR_EL1_IL		(1 << 25) +#define ESR_EL1_EC_SHIFT	(26)  #define ESR_EL1_EC_UNKNOWN	(0x00)  #define ESR_EL1_EC_WFI		(0x01)  #define ESR_EL1_EC_CP15_32	(0x03) @@ -42,7 +44,7 @@  #define ESR_EL1_EC_SP_ALIGN	(0x26)  #define ESR_EL1_EC_FP_EXC32	(0x28)  #define ESR_EL1_EC_FP_EXC64	(0x2C) -#define ESR_EL1_EC_SERRROR	(0x2F) +#define ESR_EL1_EC_SERROR	(0x2F)  #define ESR_EL1_EC_BREAKPT_EL0	(0x30)  #define ESR_EL1_EC_BREAKPT_EL1	(0x31)  #define ESR_EL1_EC_SOFTSTP_EL0	(0x32) diff --git a/arch/arm64/include/asm/fixmap.h b/arch/arm64/include/asm/fixmap.h new file mode 100644 index 00000000000..5f7bfe6df72 --- /dev/null +++ b/arch/arm64/include/asm/fixmap.h @@ -0,0 +1,67 @@ +/* + * fixmap.h: compile-time virtual memory allocation + * + * This file is subject to the terms and conditions of the GNU General Public + * License.  See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1998 Ingo Molnar + * Copyright (C) 2013 Mark Salter <msalter@redhat.com> + * + * Adapted from arch/x86_64 version. + * + */ + +#ifndef _ASM_ARM64_FIXMAP_H +#define _ASM_ARM64_FIXMAP_H + +#ifndef __ASSEMBLY__ +#include <linux/kernel.h> +#include <asm/page.h> + +/* + * Here we define all the compile-time 'special' virtual + * addresses. The point is to have a constant address at + * compile time, but to set the physical address only + * in the boot process. + * + * These 'compile-time allocated' memory buffers are + * page-sized. Use set_fixmap(idx,phys) to associate + * physical memory with fixmap indices. + * + */ +enum fixed_addresses { +	FIX_EARLYCON_MEM_BASE, +	__end_of_permanent_fixed_addresses, + +	/* +	 * Temporary boot-time mappings, used by early_ioremap(), +	 * before ioremap() is functional. +	 */ +#ifdef CONFIG_ARM64_64K_PAGES +#define NR_FIX_BTMAPS		4 +#else +#define NR_FIX_BTMAPS		64 +#endif +#define FIX_BTMAPS_SLOTS	7 +#define TOTAL_FIX_BTMAPS	(NR_FIX_BTMAPS * FIX_BTMAPS_SLOTS) + +	FIX_BTMAP_END = __end_of_permanent_fixed_addresses, +	FIX_BTMAP_BEGIN = FIX_BTMAP_END + TOTAL_FIX_BTMAPS - 1, +	__end_of_fixed_addresses +}; + +#define FIXADDR_SIZE	(__end_of_permanent_fixed_addresses << PAGE_SHIFT) +#define FIXADDR_START	(FIXADDR_TOP - FIXADDR_SIZE) + +#define FIXMAP_PAGE_IO     __pgprot(PROT_DEVICE_nGnRE) + +extern void __early_set_fixmap(enum fixed_addresses idx, +			       phys_addr_t phys, pgprot_t flags); + +#define __set_fixmap __early_set_fixmap + +#include <asm-generic/fixmap.h> + +#endif /* !__ASSEMBLY__ */ +#endif /* _ASM_ARM64_FIXMAP_H */ diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h index c43b4ac1300..50f559f574f 100644 --- a/arch/arm64/include/asm/fpsimd.h +++ b/arch/arm64/include/asm/fpsimd.h @@ -37,8 +37,21 @@ struct fpsimd_state {  			u32 fpcr;  		};  	}; +	/* the id of the last cpu to have restored this state */ +	unsigned int cpu;  }; +/* + * Struct for stacking the bottom 'n' FP/SIMD registers. + */ +struct fpsimd_partial_state { +	u32		fpsr; +	u32		fpcr; +	u32		num_regs; +	__uint128_t	vregs[32]; +}; + +  #if defined(__KERNEL__) && defined(CONFIG_COMPAT)  /* Masks for extracting the FPSR and FPCR from the FPSCR */  #define VFP_FPSCR_STAT_MASK	0xf800009f @@ -58,6 +71,16 @@ extern void fpsimd_load_state(struct fpsimd_state *state);  extern void fpsimd_thread_switch(struct task_struct *next);  extern void fpsimd_flush_thread(void); +extern void fpsimd_preserve_current_state(void); +extern void fpsimd_restore_current_state(void); +extern void fpsimd_update_current_state(struct fpsimd_state *state); + +extern void fpsimd_flush_task_state(struct task_struct *target); + +extern void fpsimd_save_partial_state(struct fpsimd_partial_state *state, +				      u32 num_regs); +extern void fpsimd_load_partial_state(struct fpsimd_partial_state *state); +  #endif  #endif diff --git a/arch/arm64/include/asm/fpsimdmacros.h b/arch/arm64/include/asm/fpsimdmacros.h index bbec599c96b..768414d55e6 100644 --- a/arch/arm64/include/asm/fpsimdmacros.h +++ b/arch/arm64/include/asm/fpsimdmacros.h @@ -62,3 +62,38 @@  	ldr	w\tmpnr, [\state, #16 * 2 + 4]  	msr	fpcr, x\tmpnr  .endm + +.altmacro +.macro fpsimd_save_partial state, numnr, tmpnr1, tmpnr2 +	mrs	x\tmpnr1, fpsr +	str	w\numnr, [\state, #8] +	mrs	x\tmpnr2, fpcr +	stp	w\tmpnr1, w\tmpnr2, [\state] +	adr	x\tmpnr1, 0f +	add	\state, \state, x\numnr, lsl #4 +	sub	x\tmpnr1, x\tmpnr1, x\numnr, lsl #1 +	br	x\tmpnr1 +	.irp	qa, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0 +	.irp	qb, %(qa + 1) +	stp	q\qa, q\qb, [\state, # -16 * \qa - 16] +	.endr +	.endr +0: +.endm + +.macro fpsimd_restore_partial state, tmpnr1, tmpnr2 +	ldp	w\tmpnr1, w\tmpnr2, [\state] +	msr	fpsr, x\tmpnr1 +	msr	fpcr, x\tmpnr2 +	adr	x\tmpnr1, 0f +	ldr	w\tmpnr2, [\state, #8] +	add	\state, \state, x\tmpnr2, lsl #4 +	sub	x\tmpnr1, x\tmpnr1, x\tmpnr2, lsl #1 +	br	x\tmpnr1 +	.irp	qa, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0 +	.irp	qb, %(qa + 1) +	ldp	q\qa, q\qb, [\state, # -16 * \qa - 16] +	.endr +	.endr +0: +.endm diff --git a/arch/arm64/include/asm/ftrace.h b/arch/arm64/include/asm/ftrace.h new file mode 100644 index 00000000000..c5534facf94 --- /dev/null +++ b/arch/arm64/include/asm/ftrace.h @@ -0,0 +1,59 @@ +/* + * arch/arm64/include/asm/ftrace.h + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro <takahiro.akashi@linaro.org> + * + * 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_FTRACE_H +#define __ASM_FTRACE_H + +#include <asm/insn.h> + +#define MCOUNT_ADDR		((unsigned long)_mcount) +#define MCOUNT_INSN_SIZE	AARCH64_INSN_SIZE + +#ifndef __ASSEMBLY__ +#include <linux/compat.h> + +extern void _mcount(unsigned long); +extern void *return_address(unsigned int); + +struct dyn_arch_ftrace { +	/* No extra data needed for arm64 */ +}; + +extern unsigned long ftrace_graph_call; + +static inline unsigned long ftrace_call_adjust(unsigned long addr) +{ +	/* +	 * addr is the address of the mcount call instruction. +	 * recordmcount does the necessary offset calculation. +	 */ +	return addr; +} + +#define ftrace_return_address(n) return_address(n) + +/* + * Because AArch32 mode does not share the same syscall table with AArch64, + * tracing compat syscalls may result in reporting bogus syscalls or even + * hang-up, so just do not trace them. + * See kernel/trace/trace_syscalls.c + * + * x86 code says: + * If the user realy wants these, then they should use the + * raw syscall tracepoints with filtering. + */ +#define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS +static inline bool arch_trace_is_compat_syscall(struct pt_regs *regs) +{ +	return is_compat_task(); +} +#endif /* ifndef __ASSEMBLY__ */ + +#endif /* __ASM_FTRACE_H */ diff --git a/arch/arm64/include/asm/futex.h b/arch/arm64/include/asm/futex.h index c582fa31636..5f750dc96e0 100644 --- a/arch/arm64/include/asm/futex.h +++ b/arch/arm64/include/asm/futex.h @@ -24,12 +24,14 @@  #define __futex_atomic_op(insn, ret, oldval, uaddr, tmp, oparg)		\  	asm volatile(							\ -"1:	ldaxr	%w1, %2\n"						\ +"1:	ldxr	%w1, %2\n"						\  	insn "\n"							\  "2:	stlxr	%w3, %w0, %2\n"						\  "	cbnz	%w3, 1b\n"						\ +"	dmb	ish\n"							\  "3:\n"									\  "	.pushsection .fixup,\"ax\"\n"					\ +"	.align	2\n"							\  "4:	mov	%w0, %w5\n"						\  "	b	3b\n"							\  "	.popsection\n"							\ @@ -39,7 +41,7 @@  "	.popsection\n"							\  	: "=&r" (ret), "=&r" (oldval), "+Q" (*uaddr), "=&r" (tmp)	\  	: "r" (oparg), "Ir" (-EFAULT)					\ -	: "cc", "memory") +	: "memory")  static inline int  futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr) @@ -110,11 +112,12 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,  		return -EFAULT;  	asm volatile("// futex_atomic_cmpxchg_inatomic\n" -"1:	ldaxr	%w1, %2\n" +"1:	ldxr	%w1, %2\n"  "	sub	%w3, %w1, %w4\n"  "	cbnz	%w3, 3f\n"  "2:	stlxr	%w3, %w5, %2\n"  "	cbnz	%w3, 1b\n" +"	dmb	ish\n"  "3:\n"  "	.pushsection .fixup,\"ax\"\n"  "4:	mov	%w0, %w6\n" @@ -126,7 +129,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,  "	.popsection\n"  	: "+r" (ret), "=&r" (val), "+Q" (*uaddr), "=&r" (tmp)  	: "r" (oldval), "r" (newval), "Ir" (-EFAULT) -	: "cc", "memory"); +	: "memory");  	*uval = val;  	return ret; diff --git a/arch/arm64/include/asm/hardirq.h b/arch/arm64/include/asm/hardirq.h index 990c051e782..0be67821f9c 100644 --- a/arch/arm64/include/asm/hardirq.h +++ b/arch/arm64/include/asm/hardirq.h @@ -20,7 +20,7 @@  #include <linux/threads.h>  #include <asm/irq.h> -#define NR_IPI	4 +#define NR_IPI	6  typedef struct {  	unsigned int __softirq_pending; diff --git a/arch/arm64/include/asm/hwcap.h b/arch/arm64/include/asm/hwcap.h index e2950b098e7..024c46183c3 100644 --- a/arch/arm64/include/asm/hwcap.h +++ b/arch/arm64/include/asm/hwcap.h @@ -30,6 +30,13 @@  #define COMPAT_HWCAP_IDIVA	(1 << 17)  #define COMPAT_HWCAP_IDIVT	(1 << 18)  #define COMPAT_HWCAP_IDIV	(COMPAT_HWCAP_IDIVA|COMPAT_HWCAP_IDIVT) +#define COMPAT_HWCAP_EVTSTRM	(1 << 21) + +#define COMPAT_HWCAP2_AES	(1 << 0) +#define COMPAT_HWCAP2_PMULL	(1 << 1) +#define COMPAT_HWCAP2_SHA1	(1 << 2) +#define COMPAT_HWCAP2_SHA2	(1 << 3) +#define COMPAT_HWCAP2_CRC32	(1 << 4)  #ifndef __ASSEMBLY__  /* @@ -37,11 +44,12 @@   * instruction set this cpu supports.   */  #define ELF_HWCAP		(elf_hwcap) -#define COMPAT_ELF_HWCAP	(COMPAT_HWCAP_HALF|COMPAT_HWCAP_THUMB|\ -				 COMPAT_HWCAP_FAST_MULT|COMPAT_HWCAP_EDSP|\ -				 COMPAT_HWCAP_TLS|COMPAT_HWCAP_VFP|\ -				 COMPAT_HWCAP_VFPv3|COMPAT_HWCAP_VFPv4|\ -				 COMPAT_HWCAP_NEON|COMPAT_HWCAP_IDIV) + +#ifdef CONFIG_COMPAT +#define COMPAT_ELF_HWCAP	(compat_elf_hwcap) +#define COMPAT_ELF_HWCAP2	(compat_elf_hwcap2) +extern unsigned int compat_elf_hwcap, compat_elf_hwcap2; +#endif  extern unsigned long elf_hwcap;  #endif diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h new file mode 100644 index 00000000000..dc1f73b13e7 --- /dev/null +++ b/arch/arm64/include/asm/insn.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2013 Huawei Ltd. + * Author: Jiang Liu <liuj97@gmail.com> + * + * 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_INSN_H +#define	__ASM_INSN_H +#include <linux/types.h> + +/* A64 instructions are always 32 bits. */ +#define	AARCH64_INSN_SIZE		4 + +#ifndef __ASSEMBLY__ +/* + * ARM Architecture Reference Manual for ARMv8 Profile-A, Issue A.a + * Section C3.1 "A64 instruction index by encoding": + * AArch64 main encoding table + *  Bit position + *   28 27 26 25	Encoding Group + *   0  0  -  -		Unallocated + *   1  0  0  -		Data processing, immediate + *   1  0  1  -		Branch, exception generation and system instructions + *   -  1  -  0		Loads and stores + *   -  1  0  1		Data processing - register + *   0  1  1  1		Data processing - SIMD and floating point + *   1  1  1  1		Data processing - SIMD and floating point + * "-" means "don't care" + */ +enum aarch64_insn_encoding_class { +	AARCH64_INSN_CLS_UNKNOWN,	/* UNALLOCATED */ +	AARCH64_INSN_CLS_DP_IMM,	/* Data processing - immediate */ +	AARCH64_INSN_CLS_DP_REG,	/* Data processing - register */ +	AARCH64_INSN_CLS_DP_FPSIMD,	/* Data processing - SIMD and FP */ +	AARCH64_INSN_CLS_LDST,		/* Loads and stores */ +	AARCH64_INSN_CLS_BR_SYS,	/* Branch, exception generation and +					 * system instructions */ +}; + +enum aarch64_insn_hint_op { +	AARCH64_INSN_HINT_NOP	= 0x0 << 5, +	AARCH64_INSN_HINT_YIELD	= 0x1 << 5, +	AARCH64_INSN_HINT_WFE	= 0x2 << 5, +	AARCH64_INSN_HINT_WFI	= 0x3 << 5, +	AARCH64_INSN_HINT_SEV	= 0x4 << 5, +	AARCH64_INSN_HINT_SEVL	= 0x5 << 5, +}; + +enum aarch64_insn_imm_type { +	AARCH64_INSN_IMM_ADR, +	AARCH64_INSN_IMM_26, +	AARCH64_INSN_IMM_19, +	AARCH64_INSN_IMM_16, +	AARCH64_INSN_IMM_14, +	AARCH64_INSN_IMM_12, +	AARCH64_INSN_IMM_9, +	AARCH64_INSN_IMM_MAX +}; + +enum aarch64_insn_branch_type { +	AARCH64_INSN_BRANCH_NOLINK, +	AARCH64_INSN_BRANCH_LINK, +}; + +#define	__AARCH64_INSN_FUNCS(abbr, mask, val)	\ +static __always_inline bool aarch64_insn_is_##abbr(u32 code) \ +{ return (code & (mask)) == (val); } \ +static __always_inline u32 aarch64_insn_get_##abbr##_value(void) \ +{ return (val); } + +__AARCH64_INSN_FUNCS(b,		0xFC000000, 0x14000000) +__AARCH64_INSN_FUNCS(bl,	0xFC000000, 0x94000000) +__AARCH64_INSN_FUNCS(svc,	0xFFE0001F, 0xD4000001) +__AARCH64_INSN_FUNCS(hvc,	0xFFE0001F, 0xD4000002) +__AARCH64_INSN_FUNCS(smc,	0xFFE0001F, 0xD4000003) +__AARCH64_INSN_FUNCS(brk,	0xFFE0001F, 0xD4200000) +__AARCH64_INSN_FUNCS(hint,	0xFFFFF01F, 0xD503201F) + +#undef	__AARCH64_INSN_FUNCS + +bool aarch64_insn_is_nop(u32 insn); + +int aarch64_insn_read(void *addr, u32 *insnp); +int aarch64_insn_write(void *addr, u32 insn); +enum aarch64_insn_encoding_class aarch64_get_insn_class(u32 insn); +u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, +				  u32 insn, u64 imm); +u32 aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, +				enum aarch64_insn_branch_type type); +u32 aarch64_insn_gen_hint(enum aarch64_insn_hint_op op); +u32 aarch64_insn_gen_nop(void); + +bool aarch64_insn_hotpatch_safe(u32 old_insn, u32 new_insn); + +int aarch64_insn_patch_text_nosync(void *addr, u32 insn); +int aarch64_insn_patch_text_sync(void *addrs[], u32 insns[], int cnt); +int aarch64_insn_patch_text(void *addrs[], u32 insns[], int cnt); +#endif /* __ASSEMBLY__ */ + +#endif	/* __ASM_INSN_H */ diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h index 1d12f89140b..e0ecdcf6632 100644 --- a/arch/arm64/include/asm/io.h +++ b/arch/arm64/include/asm/io.h @@ -22,10 +22,14 @@  #ifdef __KERNEL__  #include <linux/types.h> +#include <linux/blk_types.h>  #include <asm/byteorder.h>  #include <asm/barrier.h>  #include <asm/pgtable.h> +#include <asm/early_ioremap.h> + +#include <xen/xen.h>  /*   * Generic IO read/write.  These perform native-endian accesses. @@ -118,7 +122,7 @@ static inline u64 __raw_readq(const volatile void __iomem *addr)   *  I/O port access primitives.   */  #define IO_SPACE_LIMIT		0xffff -#define PCI_IOBASE		((void __iomem *)(MODULES_VADDR - SZ_2M)) +#define PCI_IOBASE		((void __iomem *)(MODULES_VADDR - SZ_32M))  static inline u8 inb(unsigned long addr)  { @@ -224,21 +228,13 @@ extern void __memset_io(volatile void __iomem *, int, size_t);   */  extern void __iomem *__ioremap(phys_addr_t phys_addr, size_t size, pgprot_t prot);  extern void __iounmap(volatile void __iomem *addr); - -#define PROT_DEFAULT		(PTE_TYPE_PAGE | PTE_AF | PTE_DIRTY) -#define PROT_DEVICE_nGnRE	(PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_DEVICE_nGnRE)) -#define PROT_NORMAL_NC		(PROT_DEFAULT | PTE_ATTRINDX(MT_NORMAL_NC)) -#define PROT_NORMAL		(PROT_DEFAULT | PTE_ATTRINDX(MT_NORMAL)) +extern void __iomem *ioremap_cache(phys_addr_t phys_addr, size_t size);  #define ioremap(addr, size)		__ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))  #define ioremap_nocache(addr, size)	__ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE))  #define ioremap_wc(addr, size)		__ioremap((addr), (size), __pgprot(PROT_NORMAL_NC)) -#define ioremap_cached(addr, size)	__ioremap((addr), (size), __pgprot(PROT_NORMAL))  #define iounmap				__iounmap -#define PROT_SECT_DEFAULT	(PMD_TYPE_SECT | PMD_SECT_AF) -#define PROT_SECT_DEVICE_nGnRE	(PROT_SECT_DEFAULT | PTE_PXN | PTE_UXN | PMD_ATTRINDX(MT_DEVICE_nGnRE)) -  #define ARCH_HAS_IOREMAP_WC  #include <asm-generic/iomap.h> @@ -263,5 +259,12 @@ extern int devmem_is_allowed(unsigned long pfn);   */  #define xlate_dev_kmem_ptr(p)	p +struct bio_vec; +extern bool xen_biovec_phys_mergeable(const struct bio_vec *vec1, +				      const struct bio_vec *vec2); +#define BIOVEC_PHYS_MERGEABLE(vec1, vec2)				\ +	(__BIOVEC_PHYS_MERGEABLE(vec1, vec2) &&				\ +	 (!xen_domain() || xen_biovec_phys_mergeable(vec1, vec2))) +  #endif	/* __KERNEL__ */  #endif	/* __ASM_IO_H */ diff --git a/arch/arm64/include/asm/irq.h b/arch/arm64/include/asm/irq.h index 0332fc077f6..e1f7ecdde11 100644 --- a/arch/arm64/include/asm/irq.h +++ b/arch/arm64/include/asm/irq.h @@ -4,6 +4,7 @@  #include <asm-generic/irq.h>  extern void (*handle_arch_irq)(struct pt_regs *); +extern void migrate_irqs(void);  extern void set_handle_irq(void (*handle_irq)(struct pt_regs *));  #endif diff --git a/arch/arm64/include/asm/irqflags.h b/arch/arm64/include/asm/irqflags.h index aa11943b850..11cc941bd10 100644 --- a/arch/arm64/include/asm/irqflags.h +++ b/arch/arm64/include/asm/irqflags.h @@ -56,6 +56,9 @@ static inline void arch_local_irq_disable(void)  #define local_fiq_enable()	asm("msr	daifclr, #1" : : : "memory")  #define local_fiq_disable()	asm("msr	daifset, #1" : : : "memory") +#define local_async_enable()	asm("msr	daifclr, #4" : : : "memory") +#define local_async_disable()	asm("msr	daifset, #4" : : : "memory") +  /*   * Save the current interrupt enable state.   */ @@ -87,5 +90,28 @@ static inline int arch_irqs_disabled_flags(unsigned long flags)  	return flags & PSR_I_BIT;  } +/* + * save and restore debug state + */ +#define local_dbg_save(flags)						\ +	do {								\ +		typecheck(unsigned long, flags);			\ +		asm volatile(						\ +		"mrs    %0, daif		// local_dbg_save\n"	\ +		"msr    daifset, #8"					\ +		: "=r" (flags) : : "memory");				\ +	} while (0) + +#define local_dbg_restore(flags)					\ +	do {								\ +		typecheck(unsigned long, flags);			\ +		asm volatile(						\ +		"msr    daif, %0		// local_dbg_restore\n"	\ +		: : "r" (flags) : "memory");				\ +	} while (0) + +#define local_dbg_enable()	asm("msr	daifclr, #8" : : : "memory") +#define local_dbg_disable()	asm("msr	daifset, #8" : : : "memory") +  #endif  #endif diff --git a/arch/arm64/include/asm/jump_label.h b/arch/arm64/include/asm/jump_label.h new file mode 100644 index 00000000000..076a1c71404 --- /dev/null +++ b/arch/arm64/include/asm/jump_label.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2013 Huawei Ltd. + * Author: Jiang Liu <liuj97@gmail.com> + * + * Based on arch/arm/include/asm/jump_label.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. + * + * 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_JUMP_LABEL_H +#define __ASM_JUMP_LABEL_H +#include <linux/types.h> +#include <asm/insn.h> + +#ifdef __KERNEL__ + +#define JUMP_LABEL_NOP_SIZE		AARCH64_INSN_SIZE + +static __always_inline bool arch_static_branch(struct static_key *key) +{ +	asm goto("1: nop\n\t" +		 ".pushsection __jump_table,  \"aw\"\n\t" +		 ".align 3\n\t" +		 ".quad 1b, %l[l_yes], %c0\n\t" +		 ".popsection\n\t" +		 :  :  "i"(key) :  : l_yes); + +	return false; +l_yes: +	return true; +} + +#endif /* __KERNEL__ */ + +typedef u64 jump_label_t; + +struct jump_entry { +	jump_label_t code; +	jump_label_t target; +	jump_label_t key; +}; + +#endif	/* __ASM_JUMP_LABEL_H */ diff --git a/arch/arm64/include/asm/kgdb.h b/arch/arm64/include/asm/kgdb.h new file mode 100644 index 00000000000..3c8aafc1082 --- /dev/null +++ b/arch/arm64/include/asm/kgdb.h @@ -0,0 +1,84 @@ +/* + * AArch64 KGDB support + * + * Based on arch/arm/include/kgdb.h + * + * Copyright (C) 2013 Cavium Inc. + * Author: Vijaya Kumar K <vijaya.kumar@caviumnetworks.com> + * + * 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 __ARM_KGDB_H +#define __ARM_KGDB_H + +#include <linux/ptrace.h> +#include <asm/debug-monitors.h> + +#ifndef	__ASSEMBLY__ + +static inline void arch_kgdb_breakpoint(void) +{ +	asm ("brk %0" : : "I" (KDBG_COMPILED_DBG_BRK_IMM)); +} + +extern void kgdb_handle_bus_error(void); +extern int kgdb_fault_expected; + +#endif /* !__ASSEMBLY__ */ + +/* + * gdb is expecting the following registers layout. + * + * General purpose regs: + *     r0-r30: 64 bit + *     sp,pc : 64 bit + *     pstate  : 64 bit + *     Total: 34 + * FPU regs: + *     f0-f31: 128 bit + *     Total: 32 + * Extra regs + *     fpsr & fpcr: 32 bit + *     Total: 2 + * + */ + +#define _GP_REGS		34 +#define _FP_REGS		32 +#define _EXTRA_REGS		2 +/* + * general purpose registers size in bytes. + * pstate is only 4 bytes. subtract 4 bytes + */ +#define GP_REG_BYTES		(_GP_REGS * 8) +#define DBG_MAX_REG_NUM		(_GP_REGS + _FP_REGS + _EXTRA_REGS) + +/* + * Size of I/O buffer for gdb packet. + * considering to hold all register contents, size is set + */ + +#define BUFMAX			2048 + +/* + * Number of bytes required for gdb_regs buffer. + * _GP_REGS: 8 bytes, _FP_REGS: 16 bytes and _EXTRA_REGS: 4 bytes each + * GDB fails to connect for size beyond this with error + * "'g' packet reply is too long" + */ + +#define NUMREGBYTES	((_GP_REGS * 8) + (_FP_REGS * 16) + \ +			(_EXTRA_REGS * 4)) + +#endif /* __ASM_KGDB_H */ diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h index a5f28e2720c..3d6903006a8 100644 --- a/arch/arm64/include/asm/kvm_arm.h +++ b/arch/arm64/include/asm/kvm_arm.h @@ -62,7 +62,9 @@   * RW:		64bit by default, can be overriden for 32bit VMs   * TAC:		Trap ACTLR   * TSC:		Trap SMC + * TVM:		Trap VM ops (until M+C set in SCTLR_EL1)   * TSW:		Trap cache operations by set/way + * TWE:		Trap WFE   * TWI:		Trap WFI   * TIDCP:	Trap L2CTLR/L2ECTLR   * BSU_IS:	Upgrade barriers to the inner shareable domain @@ -72,8 +74,9 @@   * FMO:		Override CPSR.F and enable signaling with VF   * SWIO:	Turn set/way invalidates into set/way clean+invalidate   */ -#define HCR_GUEST_FLAGS (HCR_TSC | HCR_TSW | HCR_TWI | HCR_VM | HCR_BSU_IS | \ -			 HCR_FB | HCR_TAC | HCR_AMO | HCR_IMO | HCR_FMO | \ +#define HCR_GUEST_FLAGS (HCR_TSC | HCR_TSW | HCR_TWE | HCR_TWI | HCR_VM | \ +			 HCR_TVM | HCR_BSU_IS | HCR_FB | HCR_TAC | \ +			 HCR_AMO | HCR_IMO | HCR_FMO | \  			 HCR_SWIO | HCR_TIDCP | HCR_RW)  #define HCR_VIRT_EXCP_MASK (HCR_VA | HCR_VI | HCR_VF) @@ -104,7 +107,6 @@  /* VTCR_EL2 Registers bits */  #define VTCR_EL2_PS_MASK	(7 << 16) -#define VTCR_EL2_PS_40B		(2 << 16)  #define VTCR_EL2_TG0_MASK	(1 << 14)  #define VTCR_EL2_TG0_4K		(0 << 14)  #define VTCR_EL2_TG0_64K	(1 << 14) @@ -127,10 +129,9 @@   * 64kB pages (TG0 = 1)   * 2 level page tables (SL = 1)   */ -#define VTCR_EL2_FLAGS		(VTCR_EL2_PS_40B | VTCR_EL2_TG0_64K | \ -				 VTCR_EL2_SH0_INNER | VTCR_EL2_ORGN0_WBWA | \ -				 VTCR_EL2_IRGN0_WBWA | VTCR_EL2_SL0_LVL1 | \ -				 VTCR_EL2_T0SZ_40B) +#define VTCR_EL2_FLAGS		(VTCR_EL2_TG0_64K | VTCR_EL2_SH0_INNER | \ +				 VTCR_EL2_ORGN0_WBWA | VTCR_EL2_IRGN0_WBWA | \ +				 VTCR_EL2_SL0_LVL1 | VTCR_EL2_T0SZ_40B)  #define VTTBR_X		(38 - VTCR_EL2_T0SZ_40B)  #else  /* @@ -140,10 +141,9 @@   * 4kB pages (TG0 = 0)   * 3 level page tables (SL = 1)   */ -#define VTCR_EL2_FLAGS		(VTCR_EL2_PS_40B | VTCR_EL2_TG0_4K | \ -				 VTCR_EL2_SH0_INNER | VTCR_EL2_ORGN0_WBWA | \ -				 VTCR_EL2_IRGN0_WBWA | VTCR_EL2_SL0_LVL1 | \ -				 VTCR_EL2_T0SZ_40B) +#define VTCR_EL2_FLAGS		(VTCR_EL2_TG0_4K | VTCR_EL2_SH0_INNER | \ +				 VTCR_EL2_ORGN0_WBWA | VTCR_EL2_IRGN0_WBWA | \ +				 VTCR_EL2_SL0_LVL1 | VTCR_EL2_T0SZ_40B)  #define VTTBR_X		(37 - VTCR_EL2_T0SZ_40B)  #endif @@ -229,7 +229,7 @@  #define ESR_EL2_EC_SP_ALIGN	(0x26)  #define ESR_EL2_EC_FP_EXC32	(0x28)  #define ESR_EL2_EC_FP_EXC64	(0x2C) -#define ESR_EL2_EC_SERRROR	(0x2F) +#define ESR_EL2_EC_SERROR	(0x2F)  #define ESR_EL2_EC_BREAKPT	(0x30)  #define ESR_EL2_EC_BREAKPT_HYP	(0x31)  #define ESR_EL2_EC_SOFTSTP	(0x32) @@ -242,4 +242,6 @@  #define ESR_EL2_EC_xABT_xFSR_EXTABT	0x10 +#define ESR_EL2_EC_WFI_ISS_WFE	(1 << 0) +  #endif /* __ARM64_KVM_ARM_H__ */ diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h index b25763bc0ec..9fcd54b1e16 100644 --- a/arch/arm64/include/asm/kvm_asm.h +++ b/arch/arm64/include/asm/kvm_asm.h @@ -79,7 +79,8 @@  #define c13_TID_URW	(TPIDR_EL0 * 2)	/* Thread ID, User R/W */  #define c13_TID_URO	(TPIDRRO_EL0 * 2)/* Thread ID, User R/O */  #define c13_TID_PRIV	(TPIDR_EL1 * 2)	/* Thread ID, Privileged */ -#define c10_AMAIR	(AMAIR_EL1 * 2)	/* Aux Memory Attr Indirection Reg */ +#define c10_AMAIR0	(AMAIR_EL1 * 2)	/* Aux Memory Attr Indirection Reg */ +#define c10_AMAIR1	(c10_AMAIR0 + 1)/* Aux Memory Attr Indirection Reg */  #define c14_CNTKCTL	(CNTKCTL_EL1 * 2) /* Timer Control Register (PL1) */  #define NR_CP15_REGS	(NR_SYS_REGS * 2) diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h index eec07387521..dd8ecfc3f99 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -177,4 +177,65 @@ static inline u8 kvm_vcpu_trap_get_fault(const struct kvm_vcpu *vcpu)  	return kvm_vcpu_get_hsr(vcpu) & ESR_EL2_FSC_TYPE;  } +static inline unsigned long kvm_vcpu_get_mpidr(struct kvm_vcpu *vcpu) +{ +	return vcpu_sys_reg(vcpu, MPIDR_EL1); +} + +static inline void kvm_vcpu_set_be(struct kvm_vcpu *vcpu) +{ +	if (vcpu_mode_is_32bit(vcpu)) +		*vcpu_cpsr(vcpu) |= COMPAT_PSR_E_BIT; +	else +		vcpu_sys_reg(vcpu, SCTLR_EL1) |= (1 << 25); +} + +static inline bool kvm_vcpu_is_be(struct kvm_vcpu *vcpu) +{ +	if (vcpu_mode_is_32bit(vcpu)) +		return !!(*vcpu_cpsr(vcpu) & COMPAT_PSR_E_BIT); + +	return !!(vcpu_sys_reg(vcpu, SCTLR_EL1) & (1 << 25)); +} + +static inline unsigned long vcpu_data_guest_to_host(struct kvm_vcpu *vcpu, +						    unsigned long data, +						    unsigned int len) +{ +	if (kvm_vcpu_is_be(vcpu)) { +		switch (len) { +		case 1: +			return data & 0xff; +		case 2: +			return be16_to_cpu(data & 0xffff); +		case 4: +			return be32_to_cpu(data & 0xffffffff); +		default: +			return be64_to_cpu(data); +		} +	} + +	return data;		/* Leave LE untouched */ +} + +static inline unsigned long vcpu_data_host_to_guest(struct kvm_vcpu *vcpu, +						    unsigned long data, +						    unsigned int len) +{ +	if (kvm_vcpu_is_be(vcpu)) { +		switch (len) { +		case 1: +			return data & 0xff; +		case 2: +			return cpu_to_be16(data & 0xffff); +		case 4: +			return cpu_to_be32(data & 0xffffffff); +		default: +			return cpu_to_be64(data); +		} +	} + +	return data;		/* Leave LE untouched */ +} +  #endif /* __ARM64_KVM_EMULATE_H__ */ diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index 0859a4ddd1e..92242ce0630 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -26,7 +26,12 @@  #include <asm/kvm_asm.h>  #include <asm/kvm_mmio.h> -#define KVM_MAX_VCPUS 4 +#if defined(CONFIG_KVM_ARM_MAX_VCPUS) +#define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS +#else +#define KVM_MAX_VCPUS 0 +#endif +  #define KVM_USER_MEM_SLOTS 32  #define KVM_PRIVATE_MEM_SLOTS 4  #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 @@ -34,12 +39,7 @@  #include <kvm/arm_vgic.h>  #include <kvm/arm_arch_timer.h> -#define KVM_VCPU_MAX_FEATURES 2 - -/* We don't currently support large pages. */ -#define KVM_HPAGE_GFN_SHIFT(x)	0 -#define KVM_NR_PAGE_SIZES	1 -#define KVM_PAGES_PER_HPAGE(x)	(1UL<<31) +#define KVM_VCPU_MAX_FEATURES 3  struct kvm_vcpu;  int kvm_target_cpu(void); @@ -151,6 +151,7 @@ struct kvm_vcpu_stat {  struct kvm_vcpu_init;  int kvm_vcpu_set_target(struct kvm_vcpu *vcpu,  			const struct kvm_vcpu_init *init); +int kvm_vcpu_preferred_target(struct kvm_vcpu_init *init);  unsigned long kvm_arm_num_regs(struct kvm_vcpu *vcpu);  int kvm_arm_copy_reg_indices(struct kvm_vcpu *vcpu, u64 __user *indices);  struct kvm_one_reg; diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h index efe609c6a3c..7d29847a893 100644 --- a/arch/arm64/include/asm/kvm_mmu.h +++ b/arch/arm64/include/asm/kvm_mmu.h @@ -91,6 +91,7 @@ int kvm_mmu_init(void);  void kvm_clear_hyp_idmap(void);  #define	kvm_set_pte(ptep, pte)		set_pte(ptep, pte) +#define	kvm_set_pmd(pmdp, pmd)		set_pmd(pmdp, pmd)  static inline bool kvm_is_write_fault(unsigned long esr)  { @@ -105,7 +106,6 @@ static inline bool kvm_is_write_fault(unsigned long esr)  	return true;  } -static inline void kvm_clean_dcache_area(void *addr, size_t size) {}  static inline void kvm_clean_pgd(pgd_t *pgd) {}  static inline void kvm_clean_pmd_entry(pmd_t *pmd) {}  static inline void kvm_clean_pte(pte_t *pte) {} @@ -116,20 +116,41 @@ static inline void kvm_set_s2pte_writable(pte_t *pte)  	pte_val(*pte) |= PTE_S2_RDWR;  } +static inline void kvm_set_s2pmd_writable(pmd_t *pmd) +{ +	pmd_val(*pmd) |= PMD_S2_RDWR; +} + +#define kvm_pgd_addr_end(addr, end)	pgd_addr_end(addr, end) +#define kvm_pud_addr_end(addr, end)	pud_addr_end(addr, end) +#define kvm_pmd_addr_end(addr, end)	pmd_addr_end(addr, end) +  struct kvm; -static inline void coherent_icache_guest_page(struct kvm *kvm, gfn_t gfn) +#define kvm_flush_dcache_to_poc(a,l)	__flush_dcache_area((a), (l)) + +static inline bool vcpu_has_cache_enabled(struct kvm_vcpu *vcpu)  { +	return (vcpu_sys_reg(vcpu, SCTLR_EL1) & 0b101) == 0b101; +} + +static inline void coherent_cache_guest_page(struct kvm_vcpu *vcpu, hva_t hva, +					     unsigned long size) +{ +	if (!vcpu_has_cache_enabled(vcpu)) +		kvm_flush_dcache_to_poc((void *)hva, size); +  	if (!icache_is_aliasing()) {		/* PIPT */ -		unsigned long hva = gfn_to_hva(kvm, gfn); -		flush_icache_range(hva, hva + PAGE_SIZE); +		flush_icache_range(hva, hva + size);  	} else if (!icache_is_aivivt()) {	/* non ASID-tagged VIVT */  		/* any kind of VIPT cache */  		__flush_icache_all();  	}  } -#define kvm_flush_dcache_to_poc(a,l)	__flush_dcache_area((a), (l)) +#define kvm_virt_to_phys(x)		__virt_to_phys((unsigned long)(x)) + +void stage2_flush_vm(struct kvm *kvm);  #endif /* __ASSEMBLY__ */  #endif /* __ARM64_KVM_MMU_H__ */ diff --git a/arch/arm64/include/asm/kvm_psci.h b/arch/arm64/include/asm/kvm_psci.h index e301a481635..bc39e557c56 100644 --- a/arch/arm64/include/asm/kvm_psci.h +++ b/arch/arm64/include/asm/kvm_psci.h @@ -18,6 +18,10 @@  #ifndef __ARM64_KVM_PSCI_H__  #define __ARM64_KVM_PSCI_H__ -bool kvm_psci_call(struct kvm_vcpu *vcpu); +#define KVM_ARM_PSCI_0_1	1 +#define KVM_ARM_PSCI_0_2	2 + +int kvm_psci_version(struct kvm_vcpu *vcpu); +int kvm_psci_call(struct kvm_vcpu *vcpu);  #endif /* __ARM64_KVM_PSCI_H__ */ diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h index 20925bcf4e2..902eb708804 100644 --- a/arch/arm64/include/asm/memory.h +++ b/arch/arm64/include/asm/memory.h @@ -33,24 +33,31 @@  #define UL(x) _AC(x, UL)  /* - * PAGE_OFFSET - the virtual address of the start of the kernel image. + * PAGE_OFFSET - the virtual address of the start of the kernel image (top + *		 (VA_BITS - 1))   * VA_BITS - the maximum number of bits for virtual addresses.   * TASK_SIZE - the maximum size of a user space task.   * TASK_UNMAPPED_BASE - the lower boundary of the mmap VM area.   * The module space lives between the addresses given by TASK_SIZE   * and PAGE_OFFSET - it must be within 128MB of the kernel text.   */ -#define PAGE_OFFSET		UL(0xffffffc000000000) +#ifdef CONFIG_ARM64_64K_PAGES +#define VA_BITS			(42) +#else +#define VA_BITS			(39) +#endif +#define PAGE_OFFSET		(UL(0xffffffffffffffff) << (VA_BITS - 1))  #define MODULES_END		(PAGE_OFFSET)  #define MODULES_VADDR		(MODULES_END - SZ_64M) -#define EARLYCON_IOBASE		(MODULES_VADDR - SZ_4M) -#define VA_BITS			(39) +#define FIXADDR_TOP		(MODULES_VADDR - SZ_2M - PAGE_SIZE)  #define TASK_SIZE_64		(UL(1) << VA_BITS)  #ifdef CONFIG_COMPAT  #define TASK_SIZE_32		UL(0x100000000)  #define TASK_SIZE		(test_thread_flag(TIF_32BIT) ? \  				TASK_SIZE_32 : TASK_SIZE_64) +#define TASK_SIZE_OF(tsk)	(test_tsk_thread_flag(tsk, TIF_32BIT) ? \ +				TASK_SIZE_32 : TASK_SIZE_64)  #else  #define TASK_SIZE		TASK_SIZE_64  #endif /* CONFIG_COMPAT */ @@ -133,6 +140,7 @@ static inline void *phys_to_virt(phys_addr_t x)  #define __pa(x)			__virt_to_phys((unsigned long)(x))  #define __va(x)			((void *)__phys_to_virt((phys_addr_t)(x)))  #define pfn_to_kaddr(pfn)	__va((pfn) << PAGE_SHIFT) +#define virt_to_pfn(x)      __phys_to_pfn(__virt_to_phys(x))  /*   *  virt_to_page(k)	convert a _valid_ virtual address to struct page * @@ -141,8 +149,7 @@ static inline void *phys_to_virt(phys_addr_t x)  #define ARCH_PFN_OFFSET		PHYS_PFN_OFFSET  #define virt_to_page(kaddr)	pfn_to_page(__pa(kaddr) >> PAGE_SHIFT) -#define	virt_addr_valid(kaddr)	(((void *)(kaddr) >= (void *)PAGE_OFFSET) && \ -				 ((void *)(kaddr) < (void *)high_memory)) +#define	virt_addr_valid(kaddr)	pfn_valid(__pa(kaddr) >> PAGE_SHIFT)  #endif diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index 2494fc01896..c2f006c48bd 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -22,10 +22,16 @@ typedef struct {  	void *vdso;  } mm_context_t; +#define INIT_MM_CONTEXT(name) \ +	.context.id_lock = __RAW_SPIN_LOCK_UNLOCKED(name.context.id_lock), +  #define ASID(mm)	((mm)->context.id & 0xffff)  extern void paging_init(void);  extern void setup_mm_for_reboot(void);  extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt); +extern void init_mem_pgprot(void); +/* create an identity mapping for memory (or io if map_io is true) */ +extern void create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io);  #endif diff --git a/arch/arm64/include/asm/neon.h b/arch/arm64/include/asm/neon.h index b0cc58a9778..13ce4cc18e2 100644 --- a/arch/arm64/include/asm/neon.h +++ b/arch/arm64/include/asm/neon.h @@ -8,7 +8,11 @@   * published by the Free Software Foundation.   */ +#include <linux/types.h> +  #define cpu_has_neon()		(1) -void kernel_neon_begin(void); +#define kernel_neon_begin()	kernel_neon_begin_partial(32) + +void kernel_neon_begin_partial(u32 num_regs);  void kernel_neon_end(void); diff --git a/arch/arm64/include/asm/percpu.h b/arch/arm64/include/asm/percpu.h new file mode 100644 index 00000000000..453a179469a --- /dev/null +++ b/arch/arm64/include/asm/percpu.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2013 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_PERCPU_H +#define __ASM_PERCPU_H + +#ifdef CONFIG_SMP + +static inline void set_my_cpu_offset(unsigned long off) +{ +	asm volatile("msr tpidr_el1, %0" :: "r" (off) : "memory"); +} + +static inline unsigned long __my_cpu_offset(void) +{ +	unsigned long off; +	register unsigned long *sp asm ("sp"); + +	/* +	 * We want to allow caching the value, so avoid using volatile and +	 * instead use a fake stack read to hazard against barrier(). +	 */ +	asm("mrs %0, tpidr_el1" : "=r" (off) : "Q" (*sp)); + +	return off; +} +#define __my_cpu_offset __my_cpu_offset() + +#else	/* !CONFIG_SMP */ + +#define set_my_cpu_offset(x)	do { } while (0) + +#endif /* CONFIG_SMP */ + +#include <asm-generic/percpu.h> + +#endif /* __ASM_PERCPU_H */ diff --git a/arch/arm64/include/asm/pgalloc.h b/arch/arm64/include/asm/pgalloc.h index f214069ec5d..9bea6e74a00 100644 --- a/arch/arm64/include/asm/pgalloc.h +++ b/arch/arm64/include/asm/pgalloc.h @@ -63,9 +63,12 @@ pte_alloc_one(struct mm_struct *mm, unsigned long addr)  	struct page *pte;  	pte = alloc_pages(PGALLOC_GFP, 0); -	if (pte) -		pgtable_page_ctor(pte); - +	if (!pte) +		return NULL; +	if (!pgtable_page_ctor(pte)) { +		__free_page(pte); +		return NULL; +	}  	return pte;  } diff --git a/arch/arm64/include/asm/pgtable-2level-hwdef.h b/arch/arm64/include/asm/pgtable-2level-hwdef.h index 0a8ed3f94e9..2593b490c56 100644 --- a/arch/arm64/include/asm/pgtable-2level-hwdef.h +++ b/arch/arm64/include/asm/pgtable-2level-hwdef.h @@ -21,10 +21,10 @@   * 8192 entries of 8 bytes each, occupying a 64KB page. Levels 0 and 1 are not   * used. The 2nd level table (PGD for Linux) can cover a range of 4TB, each   * entry representing 512MB. The user and kernel address spaces are limited to - * 512GB and therefore we only use 1024 entries in the PGD. + * 4TB in the 64KB page configuration.   */  #define PTRS_PER_PTE		8192 -#define PTRS_PER_PGD		1024 +#define PTRS_PER_PGD		8192  /*   * PGDIR_SHIFT determines the size a top-level page table entry can map. diff --git a/arch/arm64/include/asm/pgtable-hwdef.h b/arch/arm64/include/asm/pgtable-hwdef.h index d57e66845c8..955e8c5f0af 100644 --- a/arch/arm64/include/asm/pgtable-hwdef.h +++ b/arch/arm64/include/asm/pgtable-hwdef.h @@ -29,6 +29,8 @@   */  #define PUD_TABLE_BIT		(_AT(pgdval_t, 1) << 1) +#define PUD_TYPE_MASK		(_AT(pgdval_t, 3) << 0) +#define PUD_TYPE_SECT		(_AT(pgdval_t, 1) << 0)  /*   * Level 2 descriptor (PMD). @@ -43,7 +45,7 @@   * Section   */  #define PMD_SECT_VALID		(_AT(pmdval_t, 1) << 0) -#define PMD_SECT_PROT_NONE	(_AT(pmdval_t, 1) << 2) +#define PMD_SECT_PROT_NONE	(_AT(pmdval_t, 1) << 58)  #define PMD_SECT_USER		(_AT(pmdval_t, 1) << 6)		/* AP[1] */  #define PMD_SECT_RDONLY		(_AT(pmdval_t, 1) << 7)		/* AP[2] */  #define PMD_SECT_S		(_AT(pmdval_t, 3) << 8) @@ -85,6 +87,8 @@  #define PTE_S2_RDONLY		(_AT(pteval_t, 1) << 6)   /* HAP[2:1] */  #define PTE_S2_RDWR		(_AT(pteval_t, 3) << 6)   /* HAP[2:1] */ +#define PMD_S2_RDWR		(_AT(pmdval_t, 3) << 6)   /* HAP[2:1] */ +  /*   * Memory Attribute override for Stage-2 (MemAttr[3:0])   */ @@ -98,9 +102,9 @@  #define PTE_HYP			PTE_USER  /* - * 40-bit physical address supported. + * Highest possible physical address supported.   */ -#define PHYS_MASK_SHIFT		(40) +#define PHYS_MASK_SHIFT		(48)  #define PHYS_MASK		((UL(1) << PHYS_MASK_SHIFT) - 1)  /* @@ -118,9 +122,12 @@  #define TCR_ORGN_WBnWA		((UL(3) << 10) | (UL(3) << 26))  #define TCR_ORGN_MASK		((UL(3) << 10) | (UL(3) << 26))  #define TCR_SHARED		((UL(3) << 12) | (UL(3) << 28)) +#define TCR_TG0_4K		(UL(0) << 14)  #define TCR_TG0_64K		(UL(1) << 14) -#define TCR_TG1_64K		(UL(1) << 30) -#define TCR_IPS_40BIT		(UL(2) << 32) +#define TCR_TG0_16K		(UL(2) << 14) +#define TCR_TG1_16K		(UL(1) << 30) +#define TCR_TG1_4K		(UL(2) << 30) +#define TCR_TG1_64K		(UL(3) << 30)  #define TCR_ASID16		(UL(1) << 36)  #define TCR_TBI0		(UL(1) << 37) diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index f0bebc5e22c..e0ccceb317d 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -25,15 +25,16 @@   * Software defined PTE bits definition.   */  #define PTE_VALID		(_AT(pteval_t, 1) << 0) -#define PTE_PROT_NONE		(_AT(pteval_t, 1) << 2)	/* only when !PTE_VALID */ -#define PTE_FILE		(_AT(pteval_t, 1) << 3)	/* only when !pte_present() */ +#define PTE_FILE		(_AT(pteval_t, 1) << 2)	/* only when !pte_present() */  #define PTE_DIRTY		(_AT(pteval_t, 1) << 55)  #define PTE_SPECIAL		(_AT(pteval_t, 1) << 56) +#define PTE_WRITE		(_AT(pteval_t, 1) << 57) +#define PTE_PROT_NONE		(_AT(pteval_t, 1) << 58) /* only when !PTE_VALID */  /*   * VMALLOC and SPARSEMEM_VMEMMAP ranges.   */ -#define VMALLOC_START		UL(0xffffff8000000000) +#define VMALLOC_START		(UL(0xffffffffffffffff) << VA_BITS)  #define VMALLOC_END		(PAGE_OFFSET - UL(0x400000000) - SZ_64K)  #define vmemmap			((struct page *)(VMALLOC_END + SZ_64K)) @@ -51,66 +52,59 @@ extern void __pgd_error(const char *file, int line, unsigned long val);  #endif  #define pgd_ERROR(pgd)		__pgd_error(__FILE__, __LINE__, pgd_val(pgd)) -/* - * The pgprot_* and protection_map entries will be fixed up at runtime to - * include the cachable and bufferable bits based on memory policy, as well as - * any architecture dependent bits like global/ASID and SMP shared mapping - * bits. - */ -#define _PAGE_DEFAULT		PTE_TYPE_PAGE | PTE_AF +#ifdef CONFIG_SMP +#define PROT_DEFAULT		(PTE_TYPE_PAGE | PTE_AF | PTE_SHARED) +#define PROT_SECT_DEFAULT	(PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S) +#else +#define PROT_DEFAULT		(PTE_TYPE_PAGE | PTE_AF) +#define PROT_SECT_DEFAULT	(PMD_TYPE_SECT | PMD_SECT_AF) +#endif -extern pgprot_t pgprot_default; +#define PROT_DEVICE_nGnRE	(PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_DEVICE_nGnRE)) +#define PROT_NORMAL_NC		(PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_NORMAL_NC)) +#define PROT_NORMAL		(PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_ATTRINDX(MT_NORMAL)) -#define __pgprot_modify(prot,mask,bits) \ -	__pgprot((pgprot_val(prot) & ~(mask)) | (bits)) +#define PROT_SECT_DEVICE_nGnRE	(PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_DEVICE_nGnRE)) +#define PROT_SECT_NORMAL	(PROT_SECT_DEFAULT | PMD_SECT_PXN | PMD_SECT_UXN | PMD_ATTRINDX(MT_NORMAL)) +#define PROT_SECT_NORMAL_EXEC	(PROT_SECT_DEFAULT | PMD_SECT_UXN | PMD_ATTRINDX(MT_NORMAL)) -#define _MOD_PROT(p, b)		__pgprot_modify(p, 0, b) +#define _PAGE_DEFAULT		(PROT_DEFAULT | PTE_ATTRINDX(MT_NORMAL)) -#define PAGE_NONE		__pgprot_modify(pgprot_default, PTE_TYPE_MASK, PTE_PROT_NONE | PTE_RDONLY | PTE_PXN | PTE_UXN) -#define PAGE_SHARED		_MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_PXN | PTE_UXN) -#define PAGE_SHARED_EXEC	_MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_PXN) -#define PAGE_COPY		_MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_RDONLY) -#define PAGE_COPY_EXEC		_MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_PXN | PTE_RDONLY) -#define PAGE_READONLY		_MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_RDONLY) -#define PAGE_READONLY_EXEC	_MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_PXN | PTE_RDONLY) -#define PAGE_KERNEL		_MOD_PROT(pgprot_default, PTE_PXN | PTE_UXN | PTE_DIRTY) -#define PAGE_KERNEL_EXEC	_MOD_PROT(pgprot_default, PTE_UXN | PTE_DIRTY) +#define PAGE_KERNEL		__pgprot(_PAGE_DEFAULT | PTE_PXN | PTE_UXN | PTE_DIRTY | PTE_WRITE) +#define PAGE_KERNEL_EXEC	__pgprot(_PAGE_DEFAULT | PTE_UXN | PTE_DIRTY | PTE_WRITE) -#define PAGE_HYP		_MOD_PROT(pgprot_default, PTE_HYP) +#define PAGE_HYP		__pgprot(_PAGE_DEFAULT | PTE_HYP)  #define PAGE_HYP_DEVICE		__pgprot(PROT_DEVICE_nGnRE | PTE_HYP) -#define PAGE_S2			__pgprot_modify(pgprot_default, PTE_S2_MEMATTR_MASK, PTE_S2_MEMATTR(MT_S2_NORMAL) | PTE_S2_RDONLY) +#define PAGE_S2			__pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_NORMAL) | PTE_S2_RDONLY)  #define PAGE_S2_DEVICE		__pgprot(PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_DEVICE_nGnRE) | PTE_S2_RDWR | PTE_UXN) -#define __PAGE_NONE		__pgprot(((_PAGE_DEFAULT) & ~PTE_TYPE_MASK) | PTE_PROT_NONE | PTE_RDONLY | PTE_PXN | PTE_UXN) -#define __PAGE_SHARED		__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN) -#define __PAGE_SHARED_EXEC	__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN) -#define __PAGE_COPY		__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_RDONLY) -#define __PAGE_COPY_EXEC	__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_RDONLY) -#define __PAGE_READONLY		__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_RDONLY) -#define __PAGE_READONLY_EXEC	__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_RDONLY) - -#endif /* __ASSEMBLY__ */ - -#define __P000  __PAGE_NONE -#define __P001  __PAGE_READONLY -#define __P010  __PAGE_COPY -#define __P011  __PAGE_COPY -#define __P100  __PAGE_READONLY_EXEC -#define __P101  __PAGE_READONLY_EXEC -#define __P110  __PAGE_COPY_EXEC -#define __P111  __PAGE_COPY_EXEC - -#define __S000  __PAGE_NONE -#define __S001  __PAGE_READONLY -#define __S010  __PAGE_SHARED -#define __S011  __PAGE_SHARED -#define __S100  __PAGE_READONLY_EXEC -#define __S101  __PAGE_READONLY_EXEC -#define __S110  __PAGE_SHARED_EXEC -#define __S111  __PAGE_SHARED_EXEC +#define PAGE_NONE		__pgprot(((_PAGE_DEFAULT) & ~PTE_TYPE_MASK) | PTE_PROT_NONE | PTE_PXN | PTE_UXN) +#define PAGE_SHARED		__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_WRITE) +#define PAGE_SHARED_EXEC	__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_WRITE) +#define PAGE_COPY		__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN) +#define PAGE_COPY_EXEC		__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN) +#define PAGE_READONLY		__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN) +#define PAGE_READONLY_EXEC	__pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN) + +#define __P000  PAGE_NONE +#define __P001  PAGE_READONLY +#define __P010  PAGE_COPY +#define __P011  PAGE_COPY +#define __P100  PAGE_READONLY_EXEC +#define __P101  PAGE_READONLY_EXEC +#define __P110  PAGE_COPY_EXEC +#define __P111  PAGE_COPY_EXEC + +#define __S000  PAGE_NONE +#define __S001  PAGE_READONLY +#define __S010  PAGE_SHARED +#define __S011  PAGE_SHARED +#define __S100  PAGE_READONLY_EXEC +#define __S101  PAGE_READONLY_EXEC +#define __S110  PAGE_SHARED_EXEC +#define __S111  PAGE_SHARED_EXEC -#ifndef __ASSEMBLY__  /*   * ZERO_PAGE is a global shared page that is always zero: used   * for zero-mapped memory areas etc.. @@ -135,26 +129,57 @@ extern struct page *empty_zero_page;  /*   * The following only work if pte_present(). Undefined behaviour otherwise.   */ -#define pte_present(pte)	(pte_val(pte) & (PTE_VALID | PTE_PROT_NONE)) -#define pte_dirty(pte)		(pte_val(pte) & PTE_DIRTY) -#define pte_young(pte)		(pte_val(pte) & PTE_AF) -#define pte_special(pte)	(pte_val(pte) & PTE_SPECIAL) -#define pte_write(pte)		(!(pte_val(pte) & PTE_RDONLY)) +#define pte_present(pte)	(!!(pte_val(pte) & (PTE_VALID | PTE_PROT_NONE))) +#define pte_dirty(pte)		(!!(pte_val(pte) & PTE_DIRTY)) +#define pte_young(pte)		(!!(pte_val(pte) & PTE_AF)) +#define pte_special(pte)	(!!(pte_val(pte) & PTE_SPECIAL)) +#define pte_write(pte)		(!!(pte_val(pte) & PTE_WRITE))  #define pte_exec(pte)		(!(pte_val(pte) & PTE_UXN))  #define pte_valid_user(pte) \  	((pte_val(pte) & (PTE_VALID | PTE_USER)) == (PTE_VALID | PTE_USER)) -#define PTE_BIT_FUNC(fn,op) \ -static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; } +static inline pte_t pte_wrprotect(pte_t pte) +{ +	pte_val(pte) &= ~PTE_WRITE; +	return pte; +} -PTE_BIT_FUNC(wrprotect, |= PTE_RDONLY); -PTE_BIT_FUNC(mkwrite,   &= ~PTE_RDONLY); -PTE_BIT_FUNC(mkclean,   &= ~PTE_DIRTY); -PTE_BIT_FUNC(mkdirty,   |= PTE_DIRTY); -PTE_BIT_FUNC(mkold,     &= ~PTE_AF); -PTE_BIT_FUNC(mkyoung,   |= PTE_AF); -PTE_BIT_FUNC(mkspecial, |= PTE_SPECIAL); +static inline pte_t pte_mkwrite(pte_t pte) +{ +	pte_val(pte) |= PTE_WRITE; +	return pte; +} + +static inline pte_t pte_mkclean(pte_t pte) +{ +	pte_val(pte) &= ~PTE_DIRTY; +	return pte; +} + +static inline pte_t pte_mkdirty(pte_t pte) +{ +	pte_val(pte) |= PTE_DIRTY; +	return pte; +} + +static inline pte_t pte_mkold(pte_t pte) +{ +	pte_val(pte) &= ~PTE_AF; +	return pte; +} + +static inline pte_t pte_mkyoung(pte_t pte) +{ +	pte_val(pte) |= PTE_AF; +	return pte; +} + +static inline pte_t pte_mkspecial(pte_t pte) +{ +	pte_val(pte) |= PTE_SPECIAL; +	return pte; +}  static inline void set_pte(pte_t *ptep, pte_t pte)  { @@ -167,10 +192,12 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,  			      pte_t *ptep, pte_t pte)  {  	if (pte_valid_user(pte)) { -		if (pte_exec(pte)) +		if (!pte_special(pte) && pte_exec(pte))  			__sync_icache_dcache(pte, addr); -		if (!pte_dirty(pte)) -			pte = pte_wrprotect(pte); +		if (pte_dirty(pte) && pte_write(pte)) +			pte_val(pte) &= ~PTE_RDONLY; +		else +			pte_val(pte) |= PTE_RDONLY;  	}  	set_pte(ptep, pte); @@ -193,36 +220,36 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,  #define __HAVE_ARCH_PTE_SPECIAL -/* - * Software PMD bits for THP - */ +static inline pte_t pmd_pte(pmd_t pmd) +{ +	return __pte(pmd_val(pmd)); +} -#define PMD_SECT_DIRTY		(_AT(pmdval_t, 1) << 55) -#define PMD_SECT_SPLITTING	(_AT(pmdval_t, 1) << 57) +static inline pmd_t pte_pmd(pte_t pte) +{ +	return __pmd(pte_val(pte)); +}  /*   * THP definitions.   */ -#define pmd_young(pmd)		(pmd_val(pmd) & PMD_SECT_AF) - -#define __HAVE_ARCH_PMD_WRITE -#define pmd_write(pmd)		(!(pmd_val(pmd) & PMD_SECT_RDONLY))  #ifdef CONFIG_TRANSPARENT_HUGEPAGE  #define pmd_trans_huge(pmd)	(pmd_val(pmd) && !(pmd_val(pmd) & PMD_TABLE_BIT)) -#define pmd_trans_splitting(pmd) (pmd_val(pmd) & PMD_SECT_SPLITTING) +#define pmd_trans_splitting(pmd)	pte_special(pmd_pte(pmd))  #endif -#define PMD_BIT_FUNC(fn,op) \ -static inline pmd_t pmd_##fn(pmd_t pmd) { pmd_val(pmd) op; return pmd; } +#define pmd_young(pmd)		pte_young(pmd_pte(pmd)) +#define pmd_wrprotect(pmd)	pte_pmd(pte_wrprotect(pmd_pte(pmd))) +#define pmd_mksplitting(pmd)	pte_pmd(pte_mkspecial(pmd_pte(pmd))) +#define pmd_mkold(pmd)		pte_pmd(pte_mkold(pmd_pte(pmd))) +#define pmd_mkwrite(pmd)	pte_pmd(pte_mkwrite(pmd_pte(pmd))) +#define pmd_mkdirty(pmd)	pte_pmd(pte_mkdirty(pmd_pte(pmd))) +#define pmd_mkyoung(pmd)	pte_pmd(pte_mkyoung(pmd_pte(pmd))) +#define pmd_mknotpresent(pmd)	(__pmd(pmd_val(pmd) & ~PMD_TYPE_MASK)) -PMD_BIT_FUNC(wrprotect,	|= PMD_SECT_RDONLY); -PMD_BIT_FUNC(mkold,	&= ~PMD_SECT_AF); -PMD_BIT_FUNC(mksplitting, |= PMD_SECT_SPLITTING); -PMD_BIT_FUNC(mkwrite,   &= ~PMD_SECT_RDONLY); -PMD_BIT_FUNC(mkdirty,   |= PMD_SECT_DIRTY); -PMD_BIT_FUNC(mkyoung,   |= PMD_SECT_AF); -PMD_BIT_FUNC(mknotpresent, &= ~PMD_TYPE_MASK); +#define __HAVE_ARCH_PMD_WRITE +#define pmd_write(pmd)		pte_write(pmd_pte(pmd))  #define pmd_mkhuge(pmd)		(__pmd(pmd_val(pmd) & ~PMD_TABLE_BIT)) @@ -231,32 +258,25 @@ PMD_BIT_FUNC(mknotpresent, &= ~PMD_TYPE_MASK);  #define mk_pmd(page,prot)	pfn_pmd(page_to_pfn(page),prot)  #define pmd_page(pmd)           pfn_to_page(__phys_to_pfn(pmd_val(pmd) & PHYS_MASK)) +#define pud_pfn(pud)		(((pud_val(pud) & PUD_MASK) & PHYS_MASK) >> PAGE_SHIFT) -static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot) -{ -	const pmdval_t mask = PMD_SECT_USER | PMD_SECT_PXN | PMD_SECT_UXN | -			      PMD_SECT_RDONLY | PMD_SECT_PROT_NONE | -			      PMD_SECT_VALID; -	pmd_val(pmd) = (pmd_val(pmd) & ~mask) | (pgprot_val(newprot) & mask); -	return pmd; -} - -#define set_pmd_at(mm, addr, pmdp, pmd)	set_pmd(pmdp, pmd) +#define set_pmd_at(mm, addr, pmdp, pmd)	set_pte_at(mm, addr, (pte_t *)pmdp, pmd_pte(pmd))  static inline int has_transparent_hugepage(void)  {  	return 1;  } +#define __pgprot_modify(prot,mask,bits) \ +	__pgprot((pgprot_val(prot) & ~(mask)) | (bits)) +  /*   * Mark the prot value as uncacheable and unbufferable.   */  #define pgprot_noncached(prot) \ -	__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRnE)) +	__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRnE) | PTE_PXN | PTE_UXN)  #define pgprot_writecombine(prot) \ -	__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_GRE)) -#define pgprot_dmacoherent(prot) \ -	__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL_NC)) +	__pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL_NC) | PTE_PXN | PTE_UXN)  #define __HAVE_PHYS_MEM_ACCESS_PROT  struct file;  extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, @@ -272,11 +292,17 @@ extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,  #define pmd_sect(pmd)		((pmd_val(pmd) & PMD_TYPE_MASK) == \  				 PMD_TYPE_SECT) +#ifdef CONFIG_ARM64_64K_PAGES +#define pud_sect(pud)		(0) +#else +#define pud_sect(pud)		((pud_val(pud) & PUD_TYPE_MASK) == \ +				 PUD_TYPE_SECT) +#endif  static inline void set_pmd(pmd_t *pmdp, pmd_t pmd)  {  	*pmdp = pmd; -	dsb(); +	dsb(ishst);  }  static inline void pmd_clear(pmd_t *pmdp) @@ -306,7 +332,7 @@ static inline pte_t *pmd_page_vaddr(pmd_t pmd)  static inline void set_pud(pud_t *pudp, pud_t pud)  {  	*pudp = pud; -	dsb(); +	dsb(ishst);  }  static inline void pud_clear(pud_t *pudp) @@ -344,11 +370,16 @@ static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)  static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)  {  	const pteval_t mask = PTE_USER | PTE_PXN | PTE_UXN | PTE_RDONLY | -			      PTE_PROT_NONE | PTE_VALID; +			      PTE_PROT_NONE | PTE_VALID | PTE_WRITE;  	pte_val(pte) = (pte_val(pte) & ~mask) | (pgprot_val(newprot) & mask);  	return pte;  } +static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot) +{ +	return pte_pmd(pte_modify(pmd_pte(pmd), newprot)); +} +  extern pgd_t swapper_pg_dir[PTRS_PER_PGD];  extern pgd_t idmap_pg_dir[PTRS_PER_PGD]; @@ -357,18 +388,20 @@ extern pgd_t idmap_pg_dir[PTRS_PER_PGD];  /*   * Encode and decode a swap entry: - *	bits 0, 2:	present (must both be zero) - *	bit  3:		PTE_FILE - *	bits 4-8:	swap type - *	bits 9-63:	swap offset + *	bits 0-1:	present (must be zero) + *	bit  2:		PTE_FILE + *	bits 3-8:	swap type + *	bits 9-57:	swap offset   */ -#define __SWP_TYPE_SHIFT	4 +#define __SWP_TYPE_SHIFT	3  #define __SWP_TYPE_BITS		6 +#define __SWP_OFFSET_BITS	49  #define __SWP_TYPE_MASK		((1 << __SWP_TYPE_BITS) - 1)  #define __SWP_OFFSET_SHIFT	(__SWP_TYPE_BITS + __SWP_TYPE_SHIFT) +#define __SWP_OFFSET_MASK	((1UL << __SWP_OFFSET_BITS) - 1)  #define __swp_type(x)		(((x).val >> __SWP_TYPE_SHIFT) & __SWP_TYPE_MASK) -#define __swp_offset(x)		((x).val >> __SWP_OFFSET_SHIFT) +#define __swp_offset(x)		(((x).val >> __SWP_OFFSET_SHIFT) & __SWP_OFFSET_MASK)  #define __swp_entry(type,offset) ((swp_entry_t) { ((type) << __SWP_TYPE_SHIFT) | ((offset) << __SWP_OFFSET_SHIFT) })  #define __pte_to_swp_entry(pte)	((swp_entry_t) { pte_val(pte) }) @@ -376,21 +409,21 @@ extern pgd_t idmap_pg_dir[PTRS_PER_PGD];  /*   * Ensure that there are not more swap files than can be encoded in the kernel - * the PTEs. + * PTEs.   */  #define MAX_SWAPFILES_CHECK() BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > __SWP_TYPE_BITS)  /*   * Encode and decode a file entry: - *	bits 0, 2:	present (must both be zero) - *	bit  3:		PTE_FILE - *	bits 4-63:	file offset / PAGE_SIZE + *	bits 0-1:	present (must be zero) + *	bit  2:		PTE_FILE + *	bits 3-57:	file offset / PAGE_SIZE   */  #define pte_file(pte)		(pte_val(pte) & PTE_FILE) -#define pte_to_pgoff(x)		(pte_val(x) >> 4) -#define pgoff_to_pte(x)		__pte(((x) << 4) | PTE_FILE) +#define pte_to_pgoff(x)		(pte_val(x) >> 3) +#define pgoff_to_pte(x)		__pte(((x) << 3) | PTE_FILE) -#define PTE_FILE_MAX_BITS	60 +#define PTE_FILE_MAX_BITS	55  extern int kern_addr_valid(unsigned long addr); diff --git a/arch/arm64/include/asm/proc-fns.h b/arch/arm64/include/asm/proc-fns.h index 7cdf466fd0c..0c657bb5459 100644 --- a/arch/arm64/include/asm/proc-fns.h +++ b/arch/arm64/include/asm/proc-fns.h @@ -26,11 +26,14 @@  #include <asm/page.h>  struct mm_struct; +struct cpu_suspend_ctx;  extern void cpu_cache_off(void);  extern void cpu_do_idle(void);  extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm);  extern void cpu_reset(unsigned long addr) __attribute__((noreturn)); +extern void cpu_do_suspend(struct cpu_suspend_ctx *ptr); +extern u64 cpu_do_resume(phys_addr_t ptr, u64 idmap_ttbr);  #include <asm/memory.h> diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index ab239b2c456..34de2a8f7d9 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h @@ -79,6 +79,7 @@ struct thread_struct {  	unsigned long		tp_value;  	struct fpsimd_state	fpsimd_state;  	unsigned long		fault_address;	/* fault info */ +	unsigned long		fault_code;	/* ESR_EL1 value */  	struct debug_info	debug;		/* debugging */  }; @@ -107,6 +108,11 @@ static inline void compat_start_thread(struct pt_regs *regs, unsigned long pc,  	regs->pstate = COMPAT_PSR_MODE_USR;  	if (pc & 1)  		regs->pstate |= COMPAT_PSR_T_BIT; + +#ifdef __AARCH64EB__ +	regs->pstate |= COMPAT_PSR_E_BIT; +#endif +  	regs->compat_sp = sp;  }  #endif diff --git a/arch/arm64/include/asm/prom.h b/arch/arm64/include/asm/prom.h deleted file mode 100644 index 68b90e68295..00000000000 --- a/arch/arm64/include/asm/prom.h +++ /dev/null @@ -1 +0,0 @@ -/* Empty for now */ diff --git a/arch/arm64/include/asm/psci.h b/arch/arm64/include/asm/psci.h index 0604237ecd9..e5312ea0ec1 100644 --- a/arch/arm64/include/asm/psci.h +++ b/arch/arm64/include/asm/psci.h @@ -14,25 +14,6 @@  #ifndef __ASM_PSCI_H  #define __ASM_PSCI_H -#define PSCI_POWER_STATE_TYPE_STANDBY		0 -#define PSCI_POWER_STATE_TYPE_POWER_DOWN	1 - -struct psci_power_state { -	u16	id; -	u8	type; -	u8	affinity_level; -}; - -struct psci_operations { -	int (*cpu_suspend)(struct psci_power_state state, -			   unsigned long entry_point); -	int (*cpu_off)(struct psci_power_state state); -	int (*cpu_on)(unsigned long cpuid, unsigned long entry_point); -	int (*migrate)(unsigned long cpuid); -}; - -extern struct psci_operations psci_ops; -  int psci_init(void);  #endif /* __ASM_PSCI_H */ diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h index 0dacbbf9458..501000fadb6 100644 --- a/arch/arm64/include/asm/ptrace.h +++ b/arch/arm64/include/asm/ptrace.h @@ -21,6 +21,10 @@  #include <uapi/asm/ptrace.h> +/* Current Exception Level values, as contained in CurrentEL */ +#define CurrentEL_EL1		(1 << 2) +#define CurrentEL_EL2		(2 << 2) +  /* AArch32-specific ptrace requests */  #define COMPAT_PTRACE_GETREGS		12  #define COMPAT_PTRACE_SETREGS		13 @@ -42,6 +46,7 @@  #define COMPAT_PSR_MODE_UND	0x0000001b  #define COMPAT_PSR_MODE_SYS	0x0000001f  #define COMPAT_PSR_T_BIT	0x00000020 +#define COMPAT_PSR_E_BIT	0x00000200  #define COMPAT_PSR_F_BIT	0x00000040  #define COMPAT_PSR_I_BIT	0x00000080  #define COMPAT_PSR_A_BIT	0x00000100 @@ -67,6 +72,7 @@  /* Architecturally defined mapping between AArch32 and AArch64 registers */  #define compat_usr(x)	regs[(x)] +#define compat_fp	regs[11]  #define compat_sp	regs[13]  #define compat_lr	regs[14]  #define compat_sp_hyp	regs[15] @@ -131,7 +137,12 @@ struct pt_regs {  	(!((regs)->pstate & PSR_F_BIT))  #define user_stack_pointer(regs) \ -	((regs)->sp) +	(!compat_user_mode(regs)) ? ((regs)->sp) : ((regs)->compat_sp) + +static inline unsigned long regs_return_value(struct pt_regs *regs) +{ +	return regs->regs[0]; +}  /*   * Are the current registers suitable for user mode? (used to maintain @@ -163,7 +174,7 @@ static inline int valid_user_regs(struct user_pt_regs *regs)  	return 0;  } -#define instruction_pointer(regs)	(regs)->pc +#define instruction_pointer(regs)	((unsigned long)(regs)->pc)  #ifdef CONFIG_SMP  extern unsigned long profile_pc(struct pt_regs *regs); diff --git a/arch/arm64/include/asm/sigcontext.h b/arch/arm64/include/asm/sigcontext.h deleted file mode 100644 index dca1094acc7..00000000000 --- a/arch/arm64/include/asm/sigcontext.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2012 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_SIGCONTEXT_H -#define __ASM_SIGCONTEXT_H - -#include <uapi/asm/sigcontext.h> - -/* - * Auxiliary context saved in the sigcontext.__reserved array. Not exported to - * user space as it will change with the addition of new context. User space - * should check the magic/size information. - */ -struct aux_context { -	struct fpsimd_context fpsimd; -	/* additional context to be added before "end" */ -	struct _aarch64_ctx end; -}; -#endif diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h index 4b8023c5d14..a498f2cd2c2 100644 --- a/arch/arm64/include/asm/smp.h +++ b/arch/arm64/include/asm/smp.h @@ -60,21 +60,14 @@ struct secondary_data {  	void *stack;  };  extern struct secondary_data secondary_data; -extern void secondary_holding_pen(void); -extern volatile unsigned long secondary_holding_pen_release; +extern void secondary_entry(void);  extern void arch_send_call_function_single_ipi(int cpu);  extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); -struct device_node; +extern int __cpu_disable(void); -struct smp_enable_ops { -	const char	*name; -	int		(*init_cpu)(struct device_node *, int); -	int		(*prepare_cpu)(int); -}; - -extern const struct smp_enable_ops smp_spin_table_ops; -extern const struct smp_enable_ops smp_psci_ops; +extern void __cpu_die(unsigned int cpu); +extern void cpu_die(void);  #endif /* ifndef __ASM_SMP_H */ diff --git a/arch/arm64/include/asm/smp_plat.h b/arch/arm64/include/asm/smp_plat.h index ed43a0d2b1b..59e282311b5 100644 --- a/arch/arm64/include/asm/smp_plat.h +++ b/arch/arm64/include/asm/smp_plat.h @@ -21,6 +21,19 @@  #include <asm/types.h> +struct mpidr_hash { +	u64	mask; +	u32	shift_aff[4]; +	u32	bits; +}; + +extern struct mpidr_hash mpidr_hash; + +static inline u32 mpidr_hash_size(void) +{ +	return 1 << mpidr_hash.bits; +} +  /*   * Logical CPU mapping.   */ diff --git a/arch/arm64/include/asm/spinlock.h b/arch/arm64/include/asm/spinlock.h index 0defa0728a9..c45b7b1b719 100644 --- a/arch/arm64/include/asm/spinlock.h +++ b/arch/arm64/include/asm/spinlock.h @@ -22,17 +22,10 @@  /*   * Spinlock implementation.   * - * The old value is read exclusively and the new one, if unlocked, is written - * exclusively. In case of failure, the loop is restarted. - *   * The memory barriers are implicit with the load-acquire and store-release   * instructions. - * - * Unlocked value: 0 - * Locked value: 1   */ -#define arch_spin_is_locked(x)		((x)->lock != 0)  #define arch_spin_unlock_wait(lock) \  	do { while (arch_spin_is_locked(lock)) cpu_relax(); } while (0) @@ -41,32 +34,51 @@  static inline void arch_spin_lock(arch_spinlock_t *lock)  {  	unsigned int tmp; +	arch_spinlock_t lockval, newval;  	asm volatile( -	"	sevl\n" -	"1:	wfe\n" -	"2:	ldaxr	%w0, %1\n" -	"	cbnz	%w0, 1b\n" -	"	stxr	%w0, %w2, %1\n" -	"	cbnz	%w0, 2b\n" -	: "=&r" (tmp), "+Q" (lock->lock) -	: "r" (1) -	: "cc", "memory"); +	/* Atomically increment the next ticket. */ +"	prfm	pstl1strm, %3\n" +"1:	ldaxr	%w0, %3\n" +"	add	%w1, %w0, %w5\n" +"	stxr	%w2, %w1, %3\n" +"	cbnz	%w2, 1b\n" +	/* Did we get the lock? */ +"	eor	%w1, %w0, %w0, ror #16\n" +"	cbz	%w1, 3f\n" +	/* +	 * No: spin on the owner. Send a local event to avoid missing an +	 * unlock before the exclusive load. +	 */ +"	sevl\n" +"2:	wfe\n" +"	ldaxrh	%w2, %4\n" +"	eor	%w1, %w2, %w0, lsr #16\n" +"	cbnz	%w1, 2b\n" +	/* We got the lock. Critical section starts here. */ +"3:" +	: "=&r" (lockval), "=&r" (newval), "=&r" (tmp), "+Q" (*lock) +	: "Q" (lock->owner), "I" (1 << TICKET_SHIFT) +	: "memory");  }  static inline int arch_spin_trylock(arch_spinlock_t *lock)  {  	unsigned int tmp; +	arch_spinlock_t lockval;  	asm volatile( -	"2:	ldaxr	%w0, %1\n" -	"	cbnz	%w0, 1f\n" -	"	stxr	%w0, %w2, %1\n" -	"	cbnz	%w0, 2b\n" -	"1:\n" -	: "=&r" (tmp), "+Q" (lock->lock) -	: "r" (1) -	: "cc", "memory"); +"	prfm	pstl1strm, %2\n" +"1:	ldaxr	%w0, %2\n" +"	eor	%w1, %w0, %w0, ror #16\n" +"	cbnz	%w1, 2f\n" +"	add	%w0, %w0, %3\n" +"	stxr	%w1, %w0, %2\n" +"	cbnz	%w1, 1b\n" +"2:" +	: "=&r" (lockval), "=&r" (tmp), "+Q" (*lock) +	: "I" (1 << TICKET_SHIFT) +	: "memory");  	return !tmp;  } @@ -74,9 +86,28 @@ static inline int arch_spin_trylock(arch_spinlock_t *lock)  static inline void arch_spin_unlock(arch_spinlock_t *lock)  {  	asm volatile( -	"	stlr	%w1, %0\n" -	: "=Q" (lock->lock) : "r" (0) : "memory"); +"	stlrh	%w1, %0\n" +	: "=Q" (lock->owner) +	: "r" (lock->owner + 1) +	: "memory"); +} + +static inline int arch_spin_value_unlocked(arch_spinlock_t lock) +{ +	return lock.owner == lock.next; +} + +static inline int arch_spin_is_locked(arch_spinlock_t *lock) +{ +	return !arch_spin_value_unlocked(ACCESS_ONCE(*lock)); +} + +static inline int arch_spin_is_contended(arch_spinlock_t *lock) +{ +	arch_spinlock_t lockval = ACCESS_ONCE(*lock); +	return (lockval.next - lockval.owner) > 1;  } +#define arch_spin_is_contended	arch_spin_is_contended  /*   * Write lock implementation. @@ -101,7 +132,7 @@ static inline void arch_write_lock(arch_rwlock_t *rw)  	"	cbnz	%w0, 2b\n"  	: "=&r" (tmp), "+Q" (rw->lock)  	: "r" (0x80000000) -	: "cc", "memory"); +	: "memory");  }  static inline int arch_write_trylock(arch_rwlock_t *rw) @@ -115,7 +146,7 @@ static inline int arch_write_trylock(arch_rwlock_t *rw)  	"1:\n"  	: "=&r" (tmp), "+Q" (rw->lock)  	: "r" (0x80000000) -	: "cc", "memory"); +	: "memory");  	return !tmp;  } @@ -156,7 +187,7 @@ static inline void arch_read_lock(arch_rwlock_t *rw)  	"	cbnz	%w1, 2b\n"  	: "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock)  	: -	: "cc", "memory"); +	: "memory");  }  static inline void arch_read_unlock(arch_rwlock_t *rw) @@ -170,7 +201,7 @@ static inline void arch_read_unlock(arch_rwlock_t *rw)  	"	cbnz	%w1, 1b\n"  	: "=&r" (tmp), "=&r" (tmp2), "+Q" (rw->lock)  	: -	: "cc", "memory"); +	: "memory");  }  static inline int arch_read_trylock(arch_rwlock_t *rw) @@ -185,7 +216,7 @@ static inline int arch_read_trylock(arch_rwlock_t *rw)  	"1:\n"  	: "=&r" (tmp), "+r" (tmp2), "+Q" (rw->lock)  	: -	: "cc", "memory"); +	: "memory");  	return !tmp2;  } diff --git a/arch/arm64/include/asm/spinlock_types.h b/arch/arm64/include/asm/spinlock_types.h index 9a494346efe..b8d383665f5 100644 --- a/arch/arm64/include/asm/spinlock_types.h +++ b/arch/arm64/include/asm/spinlock_types.h @@ -20,14 +20,19 @@  # error "please don't include this file directly"  #endif -/* We only require natural alignment for exclusive accesses. */ -#define __lock_aligned +#define TICKET_SHIFT	16  typedef struct { -	volatile unsigned int lock; -} arch_spinlock_t; +#ifdef __AARCH64EB__ +	u16 next; +	u16 owner; +#else +	u16 owner; +	u16 next; +#endif +} __aligned(4) arch_spinlock_t; -#define __ARCH_SPIN_LOCK_UNLOCKED	{ 0 } +#define __ARCH_SPIN_LOCK_UNLOCKED	{ 0 , 0 }  typedef struct {  	volatile unsigned int lock; diff --git a/arch/arm64/include/asm/string.h b/arch/arm64/include/asm/string.h index 3ee8b303d9a..64d2d4884a9 100644 --- a/arch/arm64/include/asm/string.h +++ b/arch/arm64/include/asm/string.h @@ -22,6 +22,18 @@ extern char *strrchr(const char *, int c);  #define __HAVE_ARCH_STRCHR  extern char *strchr(const char *, int c); +#define __HAVE_ARCH_STRCMP +extern int strcmp(const char *, const char *); + +#define __HAVE_ARCH_STRNCMP +extern int strncmp(const char *, const char *, __kernel_size_t); + +#define __HAVE_ARCH_STRLEN +extern __kernel_size_t strlen(const char *); + +#define __HAVE_ARCH_STRNLEN +extern __kernel_size_t strnlen(const char *, __kernel_size_t); +  #define __HAVE_ARCH_MEMCPY  extern void *memcpy(void *, const void *, __kernel_size_t); @@ -34,4 +46,7 @@ extern void *memchr(const void *, int, __kernel_size_t);  #define __HAVE_ARCH_MEMSET  extern void *memset(void *, int, __kernel_size_t); +#define __HAVE_ARCH_MEMCMP +extern int memcmp(const void *, const void *, size_t); +  #endif diff --git a/arch/arm64/include/asm/suspend.h b/arch/arm64/include/asm/suspend.h new file mode 100644 index 00000000000..e9c149c042e --- /dev/null +++ b/arch/arm64/include/asm/suspend.h @@ -0,0 +1,27 @@ +#ifndef __ASM_SUSPEND_H +#define __ASM_SUSPEND_H + +#define NR_CTX_REGS 11 + +/* + * struct cpu_suspend_ctx must be 16-byte aligned since it is allocated on + * the stack, which must be 16-byte aligned on v8 + */ +struct cpu_suspend_ctx { +	/* +	 * This struct must be kept in sync with +	 * cpu_do_{suspend/resume} in mm/proc.S +	 */ +	u64 ctx_regs[NR_CTX_REGS]; +	u64 sp; +} __aligned(16); + +struct sleep_save_sp { +	phys_addr_t *save_ptr_stash; +	phys_addr_t save_ptr_stash_phys; +}; + +extern void cpu_resume(void); +extern int cpu_suspend(unsigned long); + +#endif diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index 89c047f9a97..383771eb0b8 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -18,6 +18,7 @@  #include <linux/err.h> +extern const void *sys_call_table[];  static inline int syscall_get_nr(struct task_struct *task,  				 struct pt_regs *regs) @@ -59,6 +60,9 @@ static inline void syscall_get_arguments(struct task_struct *task,  					 unsigned int i, unsigned int n,  					 unsigned long *args)  { +	if (n == 0) +		return; +  	if (i + n > SYSCALL_MAX_ARGS) {  		unsigned long *args_bad = args + SYSCALL_MAX_ARGS - i;  		unsigned int n_bad = n + i - SYSCALL_MAX_ARGS; @@ -82,6 +86,9 @@ static inline void syscall_set_arguments(struct task_struct *task,  					 unsigned int i, unsigned int n,  					 const unsigned long *args)  { +	if (n == 0) +		return; +  	if (i + n > SYSCALL_MAX_ARGS) {  		pr_warning("%s called with max args %d, handling only %d\n",  			   __func__, i + n, SYSCALL_MAX_ARGS); diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h index 23a3c4791d8..e40b6d06d51 100644 --- a/arch/arm64/include/asm/thread_info.h +++ b/arch/arm64/include/asm/thread_info.h @@ -89,25 +89,24 @@ static inline struct thread_info *current_thread_info(void)  #endif  /* - * We use bit 30 of the preempt_count to indicate that kernel - * preemption is occurring.  See <asm/hardirq.h>. - */ -#define PREEMPT_ACTIVE	0x40000000 - -/*   * thread information flags:   *  TIF_SYSCALL_TRACE	- syscall trace active + *  TIF_SYSCALL_TRACEPOINT - syscall tracepoint for ftrace + *  TIF_SYSCALL_AUDIT	- syscall auditing + *  TIF_SECOMP		- syscall secure computing   *  TIF_SIGPENDING	- signal pending   *  TIF_NEED_RESCHED	- rescheduling necessary   *  TIF_NOTIFY_RESUME	- callback before returning to user   *  TIF_USEDFPU		- FPU was used by this task this quantum (SMP) - *  TIF_POLLING_NRFLAG	- true if poll_idle() is polling TIF_NEED_RESCHED   */  #define TIF_SIGPENDING		0  #define TIF_NEED_RESCHED	1  #define TIF_NOTIFY_RESUME	2	/* callback before returning to user */ +#define TIF_FOREIGN_FPSTATE	3	/* CPU's FP state is not current's */  #define TIF_SYSCALL_TRACE	8 -#define TIF_POLLING_NRFLAG	16 +#define TIF_SYSCALL_AUDIT	9 +#define TIF_SYSCALL_TRACEPOINT	10 +#define TIF_SECCOMP		11  #define TIF_MEMDIE		18	/* is terminating due to OOM killer */  #define TIF_FREEZE		19  #define TIF_RESTORE_SIGMASK	20 @@ -118,10 +117,18 @@ static inline struct thread_info *current_thread_info(void)  #define _TIF_SIGPENDING		(1 << TIF_SIGPENDING)  #define _TIF_NEED_RESCHED	(1 << TIF_NEED_RESCHED)  #define _TIF_NOTIFY_RESUME	(1 << TIF_NOTIFY_RESUME) +#define _TIF_FOREIGN_FPSTATE	(1 << TIF_FOREIGN_FPSTATE) +#define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE) +#define _TIF_SYSCALL_AUDIT	(1 << TIF_SYSCALL_AUDIT) +#define _TIF_SYSCALL_TRACEPOINT	(1 << TIF_SYSCALL_TRACEPOINT) +#define _TIF_SECCOMP		(1 << TIF_SECCOMP)  #define _TIF_32BIT		(1 << TIF_32BIT)  #define _TIF_WORK_MASK		(_TIF_NEED_RESCHED | _TIF_SIGPENDING | \ -				 _TIF_NOTIFY_RESUME) +				 _TIF_NOTIFY_RESUME | _TIF_FOREIGN_FPSTATE) + +#define _TIF_SYSCALL_WORK	(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \ +				 _TIF_SYSCALL_TRACEPOINT | _TIF_SECCOMP)  #endif /* __KERNEL__ */  #endif /* __ASM_THREAD_INFO_H */ diff --git a/arch/arm64/include/asm/tlb.h b/arch/arm64/include/asm/tlb.h index 717031a762c..80e2c08900d 100644 --- a/arch/arm64/include/asm/tlb.h +++ b/arch/arm64/include/asm/tlb.h @@ -19,115 +19,45 @@  #ifndef __ASM_TLB_H  #define __ASM_TLB_H -#include <linux/pagemap.h> -#include <linux/swap.h> +#define  __tlb_remove_pmd_tlb_entry __tlb_remove_pmd_tlb_entry -#include <asm/pgalloc.h> -#include <asm/tlbflush.h> - -#define MMU_GATHER_BUNDLE	8 - -/* - * TLB handling.  This allows us to remove pages from the page - * tables, and efficiently handle the TLB issues. - */ -struct mmu_gather { -	struct mm_struct	*mm; -	unsigned int		fullmm; -	struct vm_area_struct	*vma; -	unsigned long		start, end; -	unsigned long		range_start; -	unsigned long		range_end; -	unsigned int		nr; -	unsigned int		max; -	struct page		**pages; -	struct page		*local[MMU_GATHER_BUNDLE]; -}; +#include <asm-generic/tlb.h>  /* - * This is unnecessarily complex.  There's three ways the TLB shootdown - * code is used: + * There's three ways the TLB shootdown code is used:   *  1. Unmapping a range of vmas.  See zap_page_range(), unmap_region().   *     tlb->fullmm = 0, and tlb_start_vma/tlb_end_vma will be called. - *     tlb->vma will be non-NULL.   *  2. Unmapping all vmas.  See exit_mmap().   *     tlb->fullmm = 1, and tlb_start_vma/tlb_end_vma will be called. - *     tlb->vma will be non-NULL.  Additionally, page tables will be freed. + *     Page tables will be freed.   *  3. Unmapping argument pages.  See shift_arg_pages().   *     tlb->fullmm = 0, but tlb_start_vma/tlb_end_vma will not be called. - *     tlb->vma will be NULL.   */  static inline void tlb_flush(struct mmu_gather *tlb)  { -	if (tlb->fullmm || !tlb->vma) +	if (tlb->fullmm) {  		flush_tlb_mm(tlb->mm); -	else if (tlb->range_end > 0) { -		flush_tlb_range(tlb->vma, tlb->range_start, tlb->range_end); -		tlb->range_start = TASK_SIZE; -		tlb->range_end = 0; +	} else if (tlb->end > 0) { +		struct vm_area_struct vma = { .vm_mm = tlb->mm, }; +		flush_tlb_range(&vma, tlb->start, tlb->end); +		tlb->start = TASK_SIZE; +		tlb->end = 0;  	}  }  static inline void tlb_add_flush(struct mmu_gather *tlb, unsigned long addr)  {  	if (!tlb->fullmm) { -		if (addr < tlb->range_start) -			tlb->range_start = addr; -		if (addr + PAGE_SIZE > tlb->range_end) -			tlb->range_end = addr + PAGE_SIZE; +		tlb->start = min(tlb->start, addr); +		tlb->end = max(tlb->end, addr + PAGE_SIZE);  	}  } -static inline void __tlb_alloc_page(struct mmu_gather *tlb) -{ -	unsigned long addr = __get_free_pages(GFP_NOWAIT | __GFP_NOWARN, 0); - -	if (addr) { -		tlb->pages = (void *)addr; -		tlb->max = PAGE_SIZE / sizeof(struct page *); -	} -} - -static inline void tlb_flush_mmu(struct mmu_gather *tlb) -{ -	tlb_flush(tlb); -	free_pages_and_swap_cache(tlb->pages, tlb->nr); -	tlb->nr = 0; -	if (tlb->pages == tlb->local) -		__tlb_alloc_page(tlb); -} - -static inline void -tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned long start, unsigned long end) -{ -	tlb->mm = mm; -	tlb->fullmm = !(start | (end+1)); -	tlb->start = start; -	tlb->end = end; -	tlb->vma = NULL; -	tlb->max = ARRAY_SIZE(tlb->local); -	tlb->pages = tlb->local; -	tlb->nr = 0; -	__tlb_alloc_page(tlb); -} - -static inline void -tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end) -{ -	tlb_flush_mmu(tlb); - -	/* keep the page table cache within bounds */ -	check_pgt_cache(); - -	if (tlb->pages != tlb->local) -		free_pages((unsigned long)tlb->pages, 0); -} -  /*   * Memorize the range for the TLB flush.   */ -static inline void -tlb_remove_tlb_entry(struct mmu_gather *tlb, pte_t *ptep, unsigned long addr) +static inline void __tlb_remove_tlb_entry(struct mmu_gather *tlb, pte_t *ptep, +					  unsigned long addr)  {  	tlb_add_flush(tlb, addr);  } @@ -137,38 +67,24 @@ tlb_remove_tlb_entry(struct mmu_gather *tlb, pte_t *ptep, unsigned long addr)   * case where we're doing a full MM flush.  When we're doing a munmap,   * the vmas are adjusted to only cover the region to be torn down.   */ -static inline void -tlb_start_vma(struct mmu_gather *tlb, struct vm_area_struct *vma) +static inline void tlb_start_vma(struct mmu_gather *tlb, +				 struct vm_area_struct *vma)  {  	if (!tlb->fullmm) { -		tlb->vma = vma; -		tlb->range_start = TASK_SIZE; -		tlb->range_end = 0; +		tlb->start = TASK_SIZE; +		tlb->end = 0;  	}  } -static inline void -tlb_end_vma(struct mmu_gather *tlb, struct vm_area_struct *vma) +static inline void tlb_end_vma(struct mmu_gather *tlb, +			       struct vm_area_struct *vma)  {  	if (!tlb->fullmm)  		tlb_flush(tlb);  } -static inline int __tlb_remove_page(struct mmu_gather *tlb, struct page *page) -{ -	tlb->pages[tlb->nr++] = page; -	VM_BUG_ON(tlb->nr > tlb->max); -	return tlb->max - tlb->nr; -} - -static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page) -{ -	if (!__tlb_remove_page(tlb, page)) -		tlb_flush_mmu(tlb); -} -  static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte, -	unsigned long addr) +				  unsigned long addr)  {  	pgtable_page_dtor(pte);  	tlb_add_flush(tlb, addr); @@ -184,16 +100,10 @@ static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp,  }  #endif -#define pte_free_tlb(tlb, ptep, addr)	__pte_free_tlb(tlb, ptep, addr) -#define pmd_free_tlb(tlb, pmdp, addr)	__pmd_free_tlb(tlb, pmdp, addr) -#define pud_free_tlb(tlb, pudp, addr)	pud_free((tlb)->mm, pudp) - -#define tlb_migrate_finish(mm)		do { } while (0) - -static inline void -tlb_remove_pmd_tlb_entry(struct mmu_gather *tlb, pmd_t *pmdp, unsigned long addr) +static inline void __tlb_remove_pmd_tlb_entry(struct mmu_gather *tlb, pmd_t *pmdp, +						unsigned long address)  { -	tlb_add_flush(tlb, addr); +	tlb_add_flush(tlb, address);  }  #endif diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h index 8b482035cfc..b9349c4513e 100644 --- a/arch/arm64/include/asm/tlbflush.h +++ b/arch/arm64/include/asm/tlbflush.h @@ -72,9 +72,9 @@ extern struct cpu_tlb_fns cpu_tlb;   */  static inline void flush_tlb_all(void)  { -	dsb(); +	dsb(ishst);  	asm("tlbi	vmalle1is"); -	dsb(); +	dsb(ish);  	isb();  } @@ -82,9 +82,9 @@ static inline void flush_tlb_mm(struct mm_struct *mm)  {  	unsigned long asid = (unsigned long)ASID(mm) << 48; -	dsb(); +	dsb(ishst);  	asm("tlbi	aside1is, %0" : : "r" (asid)); -	dsb(); +	dsb(ish);  }  static inline void flush_tlb_page(struct vm_area_struct *vma, @@ -93,16 +93,36 @@ static inline void flush_tlb_page(struct vm_area_struct *vma,  	unsigned long addr = uaddr >> 12 |  		((unsigned long)ASID(vma->vm_mm) << 48); -	dsb(); +	dsb(ishst);  	asm("tlbi	vae1is, %0" : : "r" (addr)); -	dsb(); +	dsb(ish);  } -/* - * Convert calls to our calling convention. - */ -#define flush_tlb_range(vma,start,end)	__cpu_flush_user_tlb_range(start,end,vma) -#define flush_tlb_kernel_range(s,e)	__cpu_flush_kern_tlb_range(s,e) +static inline void flush_tlb_range(struct vm_area_struct *vma, +					unsigned long start, unsigned long end) +{ +	unsigned long asid = (unsigned long)ASID(vma->vm_mm) << 48; +	unsigned long addr; +	start = asid | (start >> 12); +	end = asid | (end >> 12); + +	dsb(ishst); +	for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12)) +		asm("tlbi vae1is, %0" : : "r"(addr)); +	dsb(ish); +} + +static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end) +{ +	unsigned long addr; +	start >>= 12; +	end >>= 12; + +	dsb(ishst); +	for (addr = start; addr < end; addr += 1 << (PAGE_SHIFT - 12)) +		asm("tlbi vaae1is, %0" : : "r"(addr)); +	dsb(ish); +}  /*   * On AArch64, the cache coherency is handled via the set_pte_at() function. @@ -114,7 +134,7 @@ static inline void update_mmu_cache(struct vm_area_struct *vma,  	 * set_pte() does not have a DSB, so make sure that the page table  	 * write is visible.  	 */ -	dsb(); +	dsb(ishst);  }  #define update_mmu_cache_pmd(vma, address, pmd) do { } while (0) diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h new file mode 100644 index 00000000000..7ebcd31ce51 --- /dev/null +++ b/arch/arm64/include/asm/topology.h @@ -0,0 +1,36 @@ +#ifndef __ASM_TOPOLOGY_H +#define __ASM_TOPOLOGY_H + +#ifdef CONFIG_SMP + +#include <linux/cpumask.h> + +struct cpu_topology { +	int thread_id; +	int core_id; +	int cluster_id; +	cpumask_t thread_sibling; +	cpumask_t core_sibling; +}; + +extern struct cpu_topology cpu_topology[NR_CPUS]; + +#define topology_physical_package_id(cpu)	(cpu_topology[cpu].cluster_id) +#define topology_core_id(cpu)		(cpu_topology[cpu].core_id) +#define topology_core_cpumask(cpu)	(&cpu_topology[cpu].core_sibling) +#define topology_thread_cpumask(cpu)	(&cpu_topology[cpu].thread_sibling) + +void init_cpu_topology(void); +void store_cpu_topology(unsigned int cpuid); +const struct cpumask *cpu_coregroup_mask(int cpu); + +#else + +static inline void init_cpu_topology(void) { } +static inline void store_cpu_topology(unsigned int cpuid) { } + +#endif + +#include <asm-generic/topology.h> + +#endif /* _ASM_ARM_TOPOLOGY_H */ diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index edb3d5c73a3..3bf8f4e99a5 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -83,7 +83,7 @@ static inline void set_fs(mm_segment_t fs)   * Returns 1 if the range is valid, 0 otherwise.   *   * This is equivalent to the following test: - * (u65)addr + (u65)size < (u65)current->addr_limit + * (u65)addr + (u65)size <= current->addr_limit   *   * This needs 65-bit arithmetic.   */ @@ -91,7 +91,7 @@ static inline void set_fs(mm_segment_t fs)  ({									\  	unsigned long flag, roksum;					\  	__chk_user_ptr(addr);						\ -	asm("adds %1, %1, %3; ccmp %1, %4, #2, cc; cset %0, cc"		\ +	asm("adds %1, %1, %3; ccmp %1, %4, #2, cc; cset %0, ls"		\  		: "=&r" (flag), "=&r" (roksum)				\  		: "1" (addr), "Ir" (size),				\  		  "r" (current_thread_info()->addr_limit)		\ @@ -100,6 +100,7 @@ static inline void set_fs(mm_segment_t fs)  })  #define access_ok(type, addr, size)	__range_ok(addr, size) +#define user_addr_max			get_fs  /*   * The "__xxx" versions of the user access functions do not verify the address @@ -166,9 +167,10 @@ do {									\  #define get_user(x, ptr)						\  ({									\ +	__typeof__(*(ptr)) __user *__p = (ptr);				\  	might_fault();							\ -	access_ok(VERIFY_READ, (ptr), sizeof(*(ptr))) ?			\ -		__get_user((x), (ptr)) :				\ +	access_ok(VERIFY_READ, __p, sizeof(*__p)) ?			\ +		__get_user((x), __p) :					\  		((x) = 0, -EFAULT);					\  }) @@ -227,9 +229,10 @@ do {									\  #define put_user(x, ptr)						\  ({									\ +	__typeof__(*(ptr)) __user *__p = (ptr);				\  	might_fault();							\ -	access_ok(VERIFY_WRITE, (ptr), sizeof(*(ptr))) ?		\ -		__put_user((x), (ptr)) :				\ +	access_ok(VERIFY_WRITE, __p, sizeof(*__p)) ?			\ +		__put_user((x), __p) :					\  		-EFAULT;						\  }) @@ -238,9 +241,6 @@ extern unsigned long __must_check __copy_to_user(void __user *to, const void *fr  extern unsigned long __must_check __copy_in_user(void __user *to, const void __user *from, unsigned long n);  extern unsigned long __must_check __clear_user(void __user *addr, unsigned long n); -extern unsigned long __must_check __strncpy_from_user(char *to, const char __user *from, unsigned long count); -extern unsigned long __must_check __strnlen_user(const char __user *s, long n); -  static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)  {  	if (access_ok(VERIFY_READ, from, n)) @@ -274,24 +274,9 @@ static inline unsigned long __must_check clear_user(void __user *to, unsigned lo  	return n;  } -static inline long __must_check strncpy_from_user(char *dst, const char __user *src, long count) -{ -	long res = -EFAULT; -	if (access_ok(VERIFY_READ, src, 1)) -		res = __strncpy_from_user(dst, src, count); -	return res; -} - -#define strlen_user(s)	strnlen_user(s, ~0UL >> 1) +extern long strncpy_from_user(char *dest, const char __user *src, long count); -static inline long __must_check strnlen_user(const char __user *s, long n) -{ -	unsigned long res = 0; - -	if (__addr_ok(s)) -		res = __strnlen_user(s, n); - -	return res; -} +extern __must_check long strlen_user(const char __user *str); +extern __must_check long strnlen_user(const char __user *str, long n);  #endif /* __ASM_UACCESS_H */ diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h index 82ce217e94c..e5f47df00c2 100644 --- a/arch/arm64/include/asm/unistd.h +++ b/arch/arm64/include/asm/unistd.h @@ -14,6 +14,7 @@   * along with this program.  If not, see <http://www.gnu.org/licenses/>.   */  #ifdef CONFIG_COMPAT +#define __ARCH_WANT_COMPAT_SYS_GETDENTS64  #define __ARCH_WANT_COMPAT_STAT64  #define __ARCH_WANT_SYS_GETHOSTNAME  #define __ARCH_WANT_SYS_PAUSE @@ -28,3 +29,5 @@  #endif  #define __ARCH_WANT_SYS_CLONE  #include <uapi/asm/unistd.h> + +#define NR_syscalls (__NR_syscalls) diff --git a/arch/arm64/include/asm/unistd32.h b/arch/arm64/include/asm/unistd32.h index 58125bf008d..c8d8fc17bd5 100644 --- a/arch/arm64/include/asm/unistd32.h +++ b/arch/arm64/include/asm/unistd32.h @@ -399,9 +399,13 @@ __SYSCALL(374, compat_sys_sendmmsg)  __SYSCALL(375, sys_setns)  __SYSCALL(376, compat_sys_process_vm_readv)  __SYSCALL(377, compat_sys_process_vm_writev) -__SYSCALL(378, sys_ni_syscall)			/* 378 for kcmp */ +__SYSCALL(378, sys_kcmp) +__SYSCALL(379, sys_finit_module) +__SYSCALL(380, sys_sched_setattr) +__SYSCALL(381, sys_sched_getattr) +__SYSCALL(382, sys_renameat2) -#define __NR_compat_syscalls		379 +#define __NR_compat_syscalls		383  /*   * Compat syscall numbers used by the AArch64 kernel. diff --git a/arch/arm64/include/asm/virt.h b/arch/arm64/include/asm/virt.h index 26e310c5434..215ad4649dd 100644 --- a/arch/arm64/include/asm/virt.h +++ b/arch/arm64/include/asm/virt.h @@ -18,10 +18,10 @@  #ifndef __ASM__VIRT_H  #define __ASM__VIRT_H -#define BOOT_CPU_MODE_EL2	(0x0e12b007) +#define BOOT_CPU_MODE_EL1	(0xe11) +#define BOOT_CPU_MODE_EL2	(0xe12)  #ifndef __ASSEMBLY__ -#include <asm/cacheflush.h>  /*   * __boot_cpu_mode records what mode CPUs were booted in. @@ -37,20 +37,9 @@ extern u32 __boot_cpu_mode[2];  void __hyp_set_vectors(phys_addr_t phys_vector_base);  phys_addr_t __hyp_get_vectors(void); -static inline void sync_boot_mode(void) -{ -	/* -	 * As secondaries write to __boot_cpu_mode with caches disabled, we -	 * must flush the corresponding cache entries to ensure the visibility -	 * of their writes. -	 */ -	__flush_dcache_area(__boot_cpu_mode, sizeof(__boot_cpu_mode)); -} -  /* Reports the availability of HYP mode */  static inline bool is_hyp_mode_available(void)  { -	sync_boot_mode();  	return (__boot_cpu_mode[0] == BOOT_CPU_MODE_EL2 &&  		__boot_cpu_mode[1] == BOOT_CPU_MODE_EL2);  } @@ -58,7 +47,6 @@ static inline bool is_hyp_mode_available(void)  /* Check if the bootloader has booted CPUs in different modes */  static inline bool is_hyp_mode_mismatched(void)  { -	sync_boot_mode();  	return __boot_cpu_mode[0] != __boot_cpu_mode[1];  } diff --git a/arch/arm64/include/asm/word-at-a-time.h b/arch/arm64/include/asm/word-at-a-time.h new file mode 100644 index 00000000000..aab5bf09e9d --- /dev/null +++ b/arch/arm64/include/asm/word-at-a-time.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2013 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_WORD_AT_A_TIME_H +#define __ASM_WORD_AT_A_TIME_H + +#ifndef __AARCH64EB__ + +#include <linux/kernel.h> + +struct word_at_a_time { +	const unsigned long one_bits, high_bits; +}; + +#define WORD_AT_A_TIME_CONSTANTS { REPEAT_BYTE(0x01), REPEAT_BYTE(0x80) } + +static inline unsigned long has_zero(unsigned long a, unsigned long *bits, +				     const struct word_at_a_time *c) +{ +	unsigned long mask = ((a - c->one_bits) & ~a) & c->high_bits; +	*bits = mask; +	return mask; +} + +#define prep_zero_mask(a, bits, c) (bits) + +static inline unsigned long create_zero_mask(unsigned long bits) +{ +	bits = (bits - 1) & ~bits; +	return bits >> 7; +} + +static inline unsigned long find_zero(unsigned long mask) +{ +	return fls64(mask) >> 3; +} + +#define zero_bytemask(mask) (mask) + +#else	/* __AARCH64EB__ */ +#include <asm-generic/word-at-a-time.h> +#endif + +/* + * Load an unaligned word from kernel space. + * + * In the (very unlikely) case of the word being a page-crosser + * and the next page not being mapped, take the exception and + * return zeroes in the non-existing part. + */ +static inline unsigned long load_unaligned_zeropad(const void *addr) +{ +	unsigned long ret, offset; + +	/* Load word from unaligned pointer addr */ +	asm( +	"1:	ldr	%0, %3\n" +	"2:\n" +	"	.pushsection .fixup,\"ax\"\n" +	"	.align 2\n" +	"3:	and	%1, %2, #0x7\n" +	"	bic	%2, %2, #0x7\n" +	"	ldr	%0, [%2]\n" +	"	lsl	%1, %1, #0x3\n" +#ifndef __AARCH64EB__ +	"	lsr	%0, %0, %1\n" +#else +	"	lsl	%0, %0, %1\n" +#endif +	"	b	2b\n" +	"	.popsection\n" +	"	.pushsection __ex_table,\"a\"\n" +	"	.align	3\n" +	"	.quad	1b, 3b\n" +	"	.popsection" +	: "=&r" (ret), "=&r" (offset) +	: "r" (addr), "Q" (*(unsigned long *)addr)); + +	return ret; +} + +#endif /* __ASM_WORD_AT_A_TIME_H */ diff --git a/arch/arm64/include/asm/xen/page-coherent.h b/arch/arm64/include/asm/xen/page-coherent.h new file mode 100644 index 00000000000..dde3fc9c49f --- /dev/null +++ b/arch/arm64/include/asm/xen/page-coherent.h @@ -0,0 +1,43 @@ +#ifndef _ASM_ARM64_XEN_PAGE_COHERENT_H +#define _ASM_ARM64_XEN_PAGE_COHERENT_H + +#include <asm/page.h> +#include <linux/dma-attrs.h> +#include <linux/dma-mapping.h> + +static inline void *xen_alloc_coherent_pages(struct device *hwdev, size_t size, +		dma_addr_t *dma_handle, gfp_t flags, +		struct dma_attrs *attrs) +{ +	return __generic_dma_ops(hwdev)->alloc(hwdev, size, dma_handle, flags, attrs); +} + +static inline void xen_free_coherent_pages(struct device *hwdev, size_t size, +		void *cpu_addr, dma_addr_t dma_handle, +		struct dma_attrs *attrs) +{ +	__generic_dma_ops(hwdev)->free(hwdev, size, cpu_addr, dma_handle, attrs); +} + +static inline void xen_dma_map_page(struct device *hwdev, struct page *page, +	     unsigned long offset, size_t size, enum dma_data_direction dir, +	     struct dma_attrs *attrs) +{ +} + +static inline void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle, +		size_t size, enum dma_data_direction dir, +		struct dma_attrs *attrs) +{ +} + +static inline void xen_dma_sync_single_for_cpu(struct device *hwdev, +		dma_addr_t handle, size_t size, enum dma_data_direction dir) +{ +} + +static inline void xen_dma_sync_single_for_device(struct device *hwdev, +		dma_addr_t handle, size_t size, enum dma_data_direction dir) +{ +} +#endif /* _ASM_ARM64_XEN_PAGE_COHERENT_H */ diff --git a/arch/arm64/include/uapi/asm/Kbuild b/arch/arm64/include/uapi/asm/Kbuild index e4b78bdca19..942376d37d2 100644 --- a/arch/arm64/include/uapi/asm/Kbuild +++ b/arch/arm64/include/uapi/asm/Kbuild @@ -9,6 +9,7 @@ header-y += byteorder.h  header-y += fcntl.h  header-y += hwcap.h  header-y += kvm_para.h +header-y += perf_regs.h  header-y += param.h  header-y += ptrace.h  header-y += setup.h diff --git a/arch/arm64/include/uapi/asm/byteorder.h b/arch/arm64/include/uapi/asm/byteorder.h index 2b92046aafc..dc19e9537f0 100644 --- a/arch/arm64/include/uapi/asm/byteorder.h +++ b/arch/arm64/include/uapi/asm/byteorder.h @@ -16,6 +16,10 @@  #ifndef __ASM_BYTEORDER_H  #define __ASM_BYTEORDER_H +#ifdef __AARCH64EB__ +#include <linux/byteorder/big_endian.h> +#else  #include <linux/byteorder/little_endian.h> +#endif  #endif	/* __ASM_BYTEORDER_H */ diff --git a/arch/arm64/include/uapi/asm/hwcap.h b/arch/arm64/include/uapi/asm/hwcap.h index eea497578b8..73cf0f54d57 100644 --- a/arch/arm64/include/uapi/asm/hwcap.h +++ b/arch/arm64/include/uapi/asm/hwcap.h @@ -21,6 +21,11 @@   */  #define HWCAP_FP		(1 << 0)  #define HWCAP_ASIMD		(1 << 1) - +#define HWCAP_EVTSTRM		(1 << 2) +#define HWCAP_AES		(1 << 3) +#define HWCAP_PMULL		(1 << 4) +#define HWCAP_SHA1		(1 << 5) +#define HWCAP_SHA2		(1 << 6) +#define HWCAP_CRC32		(1 << 7)  #endif /* _UAPI__ASM_HWCAP_H */ diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h index 5031f426393..e633ff8cdec 100644 --- a/arch/arm64/include/uapi/asm/kvm.h +++ b/arch/arm64/include/uapi/asm/kvm.h @@ -31,6 +31,7 @@  #define KVM_NR_SPSR	5  #ifndef __ASSEMBLY__ +#include <linux/psci.h>  #include <asm/types.h>  #include <asm/ptrace.h> @@ -55,8 +56,10 @@ struct kvm_regs {  #define KVM_ARM_TARGET_AEM_V8		0  #define KVM_ARM_TARGET_FOUNDATION_V8	1  #define KVM_ARM_TARGET_CORTEX_A57	2 +#define KVM_ARM_TARGET_XGENE_POTENZA	3 +#define KVM_ARM_TARGET_CORTEX_A53	4 -#define KVM_ARM_NUM_TARGETS		3 +#define KVM_ARM_NUM_TARGETS		5  /* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */  #define KVM_ARM_DEVICE_TYPE_SHIFT	0 @@ -76,6 +79,7 @@ struct kvm_regs {  #define KVM_ARM_VCPU_POWER_OFF		0 /* CPU is started in OFF state */  #define KVM_ARM_VCPU_EL1_32BIT		1 /* CPU running a 32bit VM */ +#define KVM_ARM_VCPU_PSCI_0_2		2 /* CPU uses PSCI v0.2 */  struct kvm_vcpu_init {  	__u32 target; @@ -129,6 +133,33 @@ struct kvm_arch_memory_slot {  #define KVM_REG_ARM64_SYSREG_OP2_MASK	0x0000000000000007  #define KVM_REG_ARM64_SYSREG_OP2_SHIFT	0 +#define ARM64_SYS_REG_SHIFT_MASK(x,n) \ +	(((x) << KVM_REG_ARM64_SYSREG_ ## n ## _SHIFT) & \ +	KVM_REG_ARM64_SYSREG_ ## n ## _MASK) + +#define __ARM64_SYS_REG(op0,op1,crn,crm,op2) \ +	(KVM_REG_ARM64 | KVM_REG_ARM64_SYSREG | \ +	ARM64_SYS_REG_SHIFT_MASK(op0, OP0) | \ +	ARM64_SYS_REG_SHIFT_MASK(op1, OP1) | \ +	ARM64_SYS_REG_SHIFT_MASK(crn, CRN) | \ +	ARM64_SYS_REG_SHIFT_MASK(crm, CRM) | \ +	ARM64_SYS_REG_SHIFT_MASK(op2, OP2)) + +#define ARM64_SYS_REG(...) (__ARM64_SYS_REG(__VA_ARGS__) | KVM_REG_SIZE_U64) + +#define KVM_REG_ARM_TIMER_CTL		ARM64_SYS_REG(3, 3, 14, 3, 1) +#define KVM_REG_ARM_TIMER_CNT		ARM64_SYS_REG(3, 3, 14, 3, 2) +#define KVM_REG_ARM_TIMER_CVAL		ARM64_SYS_REG(3, 3, 14, 0, 2) + +/* Device Control API: ARM VGIC */ +#define KVM_DEV_ARM_VGIC_GRP_ADDR	0 +#define KVM_DEV_ARM_VGIC_GRP_DIST_REGS	1 +#define KVM_DEV_ARM_VGIC_GRP_CPU_REGS	2 +#define   KVM_DEV_ARM_VGIC_CPUID_SHIFT	32 +#define   KVM_DEV_ARM_VGIC_CPUID_MASK	(0xffULL << KVM_DEV_ARM_VGIC_CPUID_SHIFT) +#define   KVM_DEV_ARM_VGIC_OFFSET_SHIFT	0 +#define   KVM_DEV_ARM_VGIC_OFFSET_MASK	(0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) +  /* KVM_IRQ_LINE irq field index values */  #define KVM_ARM_IRQ_TYPE_SHIFT		24  #define KVM_ARM_IRQ_TYPE_MASK		0xff @@ -158,10 +189,10 @@ struct kvm_arch_memory_slot {  #define KVM_PSCI_FN_CPU_ON		KVM_PSCI_FN(2)  #define KVM_PSCI_FN_MIGRATE		KVM_PSCI_FN(3) -#define KVM_PSCI_RET_SUCCESS		0 -#define KVM_PSCI_RET_NI			((unsigned long)-1) -#define KVM_PSCI_RET_INVAL		((unsigned long)-2) -#define KVM_PSCI_RET_DENIED		((unsigned long)-3) +#define KVM_PSCI_RET_SUCCESS		PSCI_RET_SUCCESS +#define KVM_PSCI_RET_NI			PSCI_RET_NOT_SUPPORTED +#define KVM_PSCI_RET_INVAL		PSCI_RET_INVALID_PARAMS +#define KVM_PSCI_RET_DENIED		PSCI_RET_DENIED  #endif diff --git a/arch/arm64/include/uapi/asm/perf_regs.h b/arch/arm64/include/uapi/asm/perf_regs.h new file mode 100644 index 00000000000..172b8317ee4 --- /dev/null +++ b/arch/arm64/include/uapi/asm/perf_regs.h @@ -0,0 +1,40 @@ +#ifndef _ASM_ARM64_PERF_REGS_H +#define _ASM_ARM64_PERF_REGS_H + +enum perf_event_arm_regs { +	PERF_REG_ARM64_X0, +	PERF_REG_ARM64_X1, +	PERF_REG_ARM64_X2, +	PERF_REG_ARM64_X3, +	PERF_REG_ARM64_X4, +	PERF_REG_ARM64_X5, +	PERF_REG_ARM64_X6, +	PERF_REG_ARM64_X7, +	PERF_REG_ARM64_X8, +	PERF_REG_ARM64_X9, +	PERF_REG_ARM64_X10, +	PERF_REG_ARM64_X11, +	PERF_REG_ARM64_X12, +	PERF_REG_ARM64_X13, +	PERF_REG_ARM64_X14, +	PERF_REG_ARM64_X15, +	PERF_REG_ARM64_X16, +	PERF_REG_ARM64_X17, +	PERF_REG_ARM64_X18, +	PERF_REG_ARM64_X19, +	PERF_REG_ARM64_X20, +	PERF_REG_ARM64_X21, +	PERF_REG_ARM64_X22, +	PERF_REG_ARM64_X23, +	PERF_REG_ARM64_X24, +	PERF_REG_ARM64_X25, +	PERF_REG_ARM64_X26, +	PERF_REG_ARM64_X27, +	PERF_REG_ARM64_X28, +	PERF_REG_ARM64_X29, +	PERF_REG_ARM64_LR, +	PERF_REG_ARM64_SP, +	PERF_REG_ARM64_PC, +	PERF_REG_ARM64_MAX, +}; +#endif /* _ASM_ARM64_PERF_REGS_H */ diff --git a/arch/arm64/include/uapi/asm/posix_types.h b/arch/arm64/include/uapi/asm/posix_types.h new file mode 100644 index 00000000000..7985ff60ca3 --- /dev/null +++ b/arch/arm64/include/uapi/asm/posix_types.h @@ -0,0 +1,10 @@ +#ifndef __ASM_POSIX_TYPES_H +#define __ASM_POSIX_TYPES_H + +typedef unsigned short __kernel_old_uid_t; +typedef unsigned short __kernel_old_gid_t; +#define __kernel_old_uid_t __kernel_old_uid_t + +#include <asm-generic/posix_types.h> + +#endif /*  __ASM_POSIX_TYPES_H */ diff --git a/arch/arm64/include/uapi/asm/sigcontext.h b/arch/arm64/include/uapi/asm/sigcontext.h index 690ad51cc90..ee469be1ae1 100644 --- a/arch/arm64/include/uapi/asm/sigcontext.h +++ b/arch/arm64/include/uapi/asm/sigcontext.h @@ -53,5 +53,12 @@ struct fpsimd_context {  	__uint128_t vregs[32];  }; +/* ESR_EL1 context */ +#define ESR_MAGIC	0x45535201 + +struct esr_context { +	struct _aarch64_ctx head; +	__u64 esr; +};  #endif /* _UAPI__ASM_SIGCONTEXT_H */ diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 7b4b564961d..cdaedad3afe 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -4,20 +4,31 @@  CPPFLAGS_vmlinux.lds	:= -DTEXT_OFFSET=$(TEXT_OFFSET)  AFLAGS_head.o		:= -DTEXT_OFFSET=$(TEXT_OFFSET) +CFLAGS_efi-stub.o 	:= -DTEXT_OFFSET=$(TEXT_OFFSET) \ +			   -I$(src)/../../../scripts/dtc/libfdt + +CFLAGS_REMOVE_ftrace.o = -pg +CFLAGS_REMOVE_insn.o = -pg +CFLAGS_REMOVE_return_address.o = -pg  # 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 +			   hyp-stub.o psci.o cpu_ops.o insn.o return_address.o  arm64-obj-$(CONFIG_COMPAT)		+= sys32.o kuser32.o signal32.o 	\  					   sys_compat.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 smp_psci.o +arm64-obj-$(CONFIG_SMP)			+= smp.o smp_spin_table.o topology.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 -arm64-obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o +arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)	+= hw_breakpoint.o +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-y					+= $(arm64-obj-y) vdso/  obj-m					+= $(arm64-obj-m) diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c index 41b4f626d55..a85843ddbde 100644 --- a/arch/arm64/kernel/arm64ksyms.c +++ b/arch/arm64/kernel/arm64ksyms.c @@ -29,16 +29,14 @@  #include <asm/checksum.h> -	/* user mem (segment) */ -EXPORT_SYMBOL(__strnlen_user); -EXPORT_SYMBOL(__strncpy_from_user); -  EXPORT_SYMBOL(copy_page);  EXPORT_SYMBOL(clear_page); +	/* user mem (segment) */  EXPORT_SYMBOL(__copy_from_user);  EXPORT_SYMBOL(__copy_to_user);  EXPORT_SYMBOL(__clear_user); +EXPORT_SYMBOL(__copy_in_user);  	/* physical memory */  EXPORT_SYMBOL(memstart_addr); @@ -46,10 +44,15 @@ EXPORT_SYMBOL(memstart_addr);  	/* string / mem functions */  EXPORT_SYMBOL(strchr);  EXPORT_SYMBOL(strrchr); +EXPORT_SYMBOL(strcmp); +EXPORT_SYMBOL(strncmp); +EXPORT_SYMBOL(strlen); +EXPORT_SYMBOL(strnlen);  EXPORT_SYMBOL(memset);  EXPORT_SYMBOL(memcpy);  EXPORT_SYMBOL(memmove);  EXPORT_SYMBOL(memchr); +EXPORT_SYMBOL(memcmp);  	/* atomic bitops */  EXPORT_SYMBOL(set_bit); @@ -58,3 +61,7 @@ EXPORT_SYMBOL(clear_bit);  EXPORT_SYMBOL(test_and_clear_bit);  EXPORT_SYMBOL(change_bit);  EXPORT_SYMBOL(test_and_change_bit); + +#ifdef CONFIG_FUNCTION_TRACER +EXPORT_SYMBOL(_mcount); +#endif diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index 666e231d410..646f888387c 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -25,6 +25,8 @@  #include <asm/thread_info.h>  #include <asm/memory.h>  #include <asm/cputable.h> +#include <asm/smp_plat.h> +#include <asm/suspend.h>  #include <asm/vdso_datapage.h>  #include <linux/kbuild.h> @@ -138,5 +140,14 @@ int main(void)    DEFINE(KVM_VTTBR,		offsetof(struct kvm, arch.vttbr));    DEFINE(KVM_VGIC_VCTRL,	offsetof(struct kvm, arch.vgic.vctrl_base));  #endif +#ifdef CONFIG_ARM64_CPU_SUSPEND +  DEFINE(CPU_SUSPEND_SZ,	sizeof(struct cpu_suspend_ctx)); +  DEFINE(CPU_CTX_SP,		offsetof(struct cpu_suspend_ctx, sp)); +  DEFINE(MPIDR_HASH_MASK,	offsetof(struct mpidr_hash, mask)); +  DEFINE(MPIDR_HASH_SHIFTS,	offsetof(struct mpidr_hash, shift_aff)); +  DEFINE(SLEEP_SAVE_SP_SZ,	sizeof(struct sleep_save_sp)); +  DEFINE(SLEEP_SAVE_SP_PHYS,	offsetof(struct sleep_save_sp, save_ptr_stash_phys)); +  DEFINE(SLEEP_SAVE_SP_VIRT,	offsetof(struct sleep_save_sp, save_ptr_stash)); +#endif    return 0;  } diff --git a/arch/arm64/kernel/cpu_ops.c b/arch/arm64/kernel/cpu_ops.c new file mode 100644 index 00000000000..d62d12fb36c --- /dev/null +++ b/arch/arm64/kernel/cpu_ops.c @@ -0,0 +1,87 @@ +/* + * CPU kernel entry/exit control + * + * Copyright (C) 2013 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/cpu_ops.h> +#include <asm/smp_plat.h> +#include <linux/errno.h> +#include <linux/of.h> +#include <linux/string.h> + +extern const struct cpu_operations smp_spin_table_ops; +extern const struct cpu_operations cpu_psci_ops; + +const struct cpu_operations *cpu_ops[NR_CPUS]; + +static const struct cpu_operations *supported_cpu_ops[] __initconst = { +#ifdef CONFIG_SMP +	&smp_spin_table_ops, +	&cpu_psci_ops, +#endif +	NULL, +}; + +static const struct cpu_operations * __init cpu_get_ops(const char *name) +{ +	const struct cpu_operations **ops = supported_cpu_ops; + +	while (*ops) { +		if (!strcmp(name, (*ops)->name)) +			return *ops; + +		ops++; +	} + +	return NULL; +} + +/* + * Read a cpu's enable method from the device tree and record it in cpu_ops. + */ +int __init cpu_read_ops(struct device_node *dn, int cpu) +{ +	const char *enable_method = of_get_property(dn, "enable-method", NULL); +	if (!enable_method) { +		/* +		 * The boot CPU may not have an enable method (e.g. when +		 * spin-table is used for secondaries). Don't warn spuriously. +		 */ +		if (cpu != 0) +			pr_err("%s: missing enable-method property\n", +				dn->full_name); +		return -ENOENT; +	} + +	cpu_ops[cpu] = cpu_get_ops(enable_method); +	if (!cpu_ops[cpu]) { +		pr_warn("%s: unsupported enable-method property: %s\n", +			dn->full_name, enable_method); +		return -EOPNOTSUPP; +	} + +	return 0; +} + +void __init cpu_read_bootcpu_ops(void) +{ +	struct device_node *dn = of_get_cpu_node(0, NULL); +	if (!dn) { +		pr_err("Failed to find device node for boot cpu\n"); +		return; +	} +	cpu_read_ops(dn, 0); +} diff --git a/arch/arm64/kernel/cputable.c b/arch/arm64/kernel/cputable.c index 63cfc4a43f4..fd3993cb060 100644 --- a/arch/arm64/kernel/cputable.c +++ b/arch/arm64/kernel/cputable.c @@ -22,7 +22,7 @@  extern unsigned long __cpu_setup(void); -struct cpu_info __initdata cpu_table[] = { +struct cpu_info cpu_table[] = {  	{  		.cpu_id_val	= 0x000f0000,  		.cpu_id_mask	= 0x000f0000, diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c index cbfacf7fb43..a7fb874b595 100644 --- a/arch/arm64/kernel/debug-monitors.c +++ b/arch/arm64/kernel/debug-monitors.c @@ -27,7 +27,6 @@  #include <linux/uaccess.h>  #include <asm/debug-monitors.h> -#include <asm/local.h>  #include <asm/cputype.h>  #include <asm/system_misc.h> @@ -89,8 +88,8 @@ early_param("nodebugmon", early_debug_disable);   * Keep track of debug users on each core.   * The ref counts are per-cpu so we use a local_t type.   */ -static DEFINE_PER_CPU(local_t, mde_ref_count); -static DEFINE_PER_CPU(local_t, kde_ref_count); +static DEFINE_PER_CPU(int, mde_ref_count); +static DEFINE_PER_CPU(int, kde_ref_count);  void enable_debug_monitors(enum debug_el el)  { @@ -98,11 +97,11 @@ void enable_debug_monitors(enum debug_el el)  	WARN_ON(preemptible()); -	if (local_inc_return(&__get_cpu_var(mde_ref_count)) == 1) +	if (this_cpu_inc_return(mde_ref_count) == 1)  		enable = DBG_MDSCR_MDE;  	if (el == DBG_ACTIVE_EL1 && -	    local_inc_return(&__get_cpu_var(kde_ref_count)) == 1) +	    this_cpu_inc_return(kde_ref_count) == 1)  		enable |= DBG_MDSCR_KDE;  	if (enable && debug_enabled) { @@ -118,11 +117,11 @@ void disable_debug_monitors(enum debug_el el)  	WARN_ON(preemptible()); -	if (local_dec_and_test(&__get_cpu_var(mde_ref_count))) +	if (this_cpu_dec_return(mde_ref_count) == 0)  		disable = ~DBG_MDSCR_MDE;  	if (el == DBG_ACTIVE_EL1 && -	    local_dec_and_test(&__get_cpu_var(kde_ref_count))) +	    this_cpu_dec_return(kde_ref_count) == 0)  		disable &= ~DBG_MDSCR_KDE;  	if (disable) { @@ -138,7 +137,6 @@ void disable_debug_monitors(enum debug_el el)  static void clear_os_lock(void *unused)  {  	asm volatile("msr oslar_el1, %0" : : "r" (0)); -	isb();  }  static int os_lock_notify(struct notifier_block *self, @@ -156,12 +154,17 @@ static struct notifier_block os_lock_nb = {  static int debug_monitors_init(void)  { +	cpu_notifier_register_begin(); +  	/* Clear the OS lock. */ -	smp_call_function(clear_os_lock, NULL, 1); -	clear_os_lock(NULL); +	on_each_cpu(clear_os_lock, NULL, 1); +	isb(); +	local_dbg_enable();  	/* Register hotplug handler. */ -	register_cpu_notifier(&os_lock_nb); +	__register_cpu_notifier(&os_lock_nb); + +	cpu_notifier_register_done();  	return 0;  }  postcore_initcall(debug_monitors_init); @@ -188,6 +191,48 @@ static void clear_regs_spsr_ss(struct pt_regs *regs)  	regs->pstate = spsr;  } +/* EL1 Single Step Handler hooks */ +static LIST_HEAD(step_hook); +static DEFINE_RWLOCK(step_hook_lock); + +void register_step_hook(struct step_hook *hook) +{ +	write_lock(&step_hook_lock); +	list_add(&hook->node, &step_hook); +	write_unlock(&step_hook_lock); +} + +void unregister_step_hook(struct step_hook *hook) +{ +	write_lock(&step_hook_lock); +	list_del(&hook->node); +	write_unlock(&step_hook_lock); +} + +/* + * Call registered single step handers + * There is no Syndrome info to check for determining the handler. + * So we call all the registered handlers, until the right handler is + * found which returns zero. + */ +static int call_step_hook(struct pt_regs *regs, unsigned int esr) +{ +	struct step_hook *hook; +	int retval = DBG_HOOK_ERROR; + +	read_lock(&step_hook_lock); + +	list_for_each_entry(hook, &step_hook, node)	{ +		retval = hook->fn(regs, esr); +		if (retval == DBG_HOOK_HANDLED) +			break; +	} + +	read_unlock(&step_hook_lock); + +	return retval; +} +  static int single_step_handler(unsigned long addr, unsigned int esr,  			       struct pt_regs *regs)  { @@ -215,7 +260,9 @@ static int single_step_handler(unsigned long addr, unsigned int esr,  		 */  		user_rewind_single_step(current);  	} else { -		/* TODO: route to KGDB */ +		if (call_step_hook(regs, esr) == DBG_HOOK_HANDLED) +			return 0; +  		pr_warning("Unexpected kernel single-step exception at EL1\n");  		/*  		 * Re-enable stepping since we know that we will be @@ -227,11 +274,50 @@ static int single_step_handler(unsigned long addr, unsigned int esr,  	return 0;  } +/* + * Breakpoint handler is re-entrant as another breakpoint can + * hit within breakpoint handler, especically in kprobes. + * Use reader/writer locks instead of plain spinlock. + */ +static LIST_HEAD(break_hook); +static DEFINE_RWLOCK(break_hook_lock); + +void register_break_hook(struct break_hook *hook) +{ +	write_lock(&break_hook_lock); +	list_add(&hook->node, &break_hook); +	write_unlock(&break_hook_lock); +} + +void unregister_break_hook(struct break_hook *hook) +{ +	write_lock(&break_hook_lock); +	list_del(&hook->node); +	write_unlock(&break_hook_lock); +} + +static int call_break_hook(struct pt_regs *regs, unsigned int esr) +{ +	struct break_hook *hook; +	int (*fn)(struct pt_regs *regs, unsigned int esr) = NULL; + +	read_lock(&break_hook_lock); +	list_for_each_entry(hook, &break_hook, node) +		if ((esr & hook->esr_mask) == hook->esr_val) +			fn = hook->fn; +	read_unlock(&break_hook_lock); + +	return fn ? fn(regs, esr) : DBG_HOOK_ERROR; +} +  static int brk_handler(unsigned long addr, unsigned int esr,  		       struct pt_regs *regs)  {  	siginfo_t info; +	if (call_break_hook(regs, esr) == DBG_HOOK_HANDLED) +		return 0; +  	if (!user_mode(regs))  		return -EFAULT; @@ -249,7 +335,8 @@ static int brk_handler(unsigned long addr, unsigned int esr,  int aarch32_break_handler(struct pt_regs *regs)  {  	siginfo_t info; -	unsigned int instr; +	u32 arm_instr; +	u16 thumb_instr;  	bool bp = false;  	void __user *pc = (void __user *)instruction_pointer(regs); @@ -258,18 +345,21 @@ int aarch32_break_handler(struct pt_regs *regs)  	if (compat_thumb_mode(regs)) {  		/* get 16-bit Thumb instruction */ -		get_user(instr, (u16 __user *)pc); -		if (instr == AARCH32_BREAK_THUMB2_LO) { +		get_user(thumb_instr, (u16 __user *)pc); +		thumb_instr = le16_to_cpu(thumb_instr); +		if (thumb_instr == AARCH32_BREAK_THUMB2_LO) {  			/* get second half of 32-bit Thumb-2 instruction */ -			get_user(instr, (u16 __user *)(pc + 2)); -			bp = instr == AARCH32_BREAK_THUMB2_HI; +			get_user(thumb_instr, (u16 __user *)(pc + 2)); +			thumb_instr = le16_to_cpu(thumb_instr); +			bp = thumb_instr == AARCH32_BREAK_THUMB2_HI;  		} else { -			bp = instr == AARCH32_BREAK_THUMB; +			bp = thumb_instr == AARCH32_BREAK_THUMB;  		}  	} else {  		/* 32-bit ARM instruction */ -		get_user(instr, (u32 __user *)pc); -		bp = (instr & ~0xf0000000) == AARCH32_BREAK_ARM; +		get_user(arm_instr, (u32 __user *)pc); +		arm_instr = le32_to_cpu(arm_instr); +		bp = (arm_instr & ~0xf0000000) == AARCH32_BREAK_ARM;  	}  	if (!bp) diff --git a/arch/arm64/kernel/early_printk.c b/arch/arm64/kernel/early_printk.c deleted file mode 100644 index fbb6e184365..00000000000 --- a/arch/arm64/kernel/early_printk.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Earlyprintk support. - * - * Copyright (C) 2012 ARM Ltd. - * Author: Catalin Marinas <catalin.marinas@arm.com> - * - * 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 <linux/kernel.h> -#include <linux/console.h> -#include <linux/init.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/io.h> - -#include <linux/amba/serial.h> -#include <linux/serial_reg.h> - -static void __iomem *early_base; -static void (*printch)(char ch); - -/* - * PL011 single character TX. - */ -static void pl011_printch(char ch) -{ -	while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_TXFF) -		; -	writeb_relaxed(ch, early_base + UART01x_DR); -	while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_BUSY) -		; -} - -/* - * Semihosting-based debug console - */ -static void smh_printch(char ch) -{ -	asm volatile("mov  x1, %0\n" -		     "mov  x0, #3\n" -		     "hlt  0xf000\n" -		     : : "r" (&ch) : "x0", "x1", "memory"); -} - -/* - * 8250/16550 (8-bit aligned registers) single character TX. - */ -static void uart8250_8bit_printch(char ch) -{ -	while (!(readb_relaxed(early_base + UART_LSR) & UART_LSR_THRE)) -		; -	writeb_relaxed(ch, early_base + UART_TX); -} - -/* - * 8250/16550 (32-bit aligned registers) single character TX. - */ -static void uart8250_32bit_printch(char ch) -{ -	while (!(readl_relaxed(early_base + (UART_LSR << 2)) & UART_LSR_THRE)) -		; -	writel_relaxed(ch, early_base + (UART_TX << 2)); -} - -struct earlycon_match { -	const char *name; -	void (*printch)(char ch); -}; - -static const struct earlycon_match earlycon_match[] __initconst = { -	{ .name = "pl011", .printch = pl011_printch, }, -	{ .name = "smh", .printch = smh_printch, }, -	{ .name = "uart8250-8bit", .printch = uart8250_8bit_printch, }, -	{ .name = "uart8250-32bit", .printch = uart8250_32bit_printch, }, -	{} -}; - -static void early_write(struct console *con, const char *s, unsigned n) -{ -	while (n-- > 0) { -		if (*s == '\n') -			printch('\r'); -		printch(*s); -		s++; -	} -} - -static struct console early_console_dev = { -	.name =		"earlycon", -	.write =	early_write, -	.flags =	CON_PRINTBUFFER | CON_BOOT, -	.index =	-1, -}; - -/* - * Parse earlyprintk=... parameter in the format: - * - *   <name>[,<addr>][,<options>] - * - * and register the early console. It is assumed that the UART has been - * initialised by the bootloader already. - */ -static int __init setup_early_printk(char *buf) -{ -	const struct earlycon_match *match = earlycon_match; -	phys_addr_t paddr = 0; - -	if (!buf) { -		pr_warning("No earlyprintk arguments passed.\n"); -		return 0; -	} - -	while (match->name) { -		size_t len = strlen(match->name); -		if (!strncmp(buf, match->name, len)) { -			buf += len; -			break; -		} -		match++; -	} -	if (!match->name) { -		pr_warning("Unknown earlyprintk arguments: %s\n", buf); -		return 0; -	} - -	/* I/O address */ -	if (!strncmp(buf, ",0x", 3)) { -		char *e; -		paddr = simple_strtoul(buf + 1, &e, 16); -		buf = e; -	} -	/* no options parsing yet */ - -	if (paddr) -		early_base = early_io_map(paddr, EARLYCON_IOBASE); - -	printch = match->printch; -	early_console = &early_console_dev; -	register_console(&early_console_dev); - -	return 0; -} - -early_param("earlyprintk", setup_early_printk); diff --git a/arch/arm64/kernel/efi-entry.S b/arch/arm64/kernel/efi-entry.S new file mode 100644 index 00000000000..619b1dd7bcd --- /dev/null +++ b/arch/arm64/kernel/efi-entry.S @@ -0,0 +1,108 @@ +/* + * EFI entry point. + * + * Copyright (C) 2013, 2014 Red Hat, Inc. + * Author: Mark Salter <msalter@redhat.com> + * + * 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/linkage.h> +#include <linux/init.h> + +#include <asm/assembler.h> + +#define EFI_LOAD_ERROR 0x8000000000000001 + +	__INIT + +	/* +	 * We arrive here from the EFI boot manager with: +	 * +	 *    * CPU in little-endian mode +	 *    * MMU on with identity-mapped RAM +	 *    * Icache and Dcache on +	 * +	 * We will most likely be running from some place other than where +	 * we want to be. The kernel image wants to be placed at TEXT_OFFSET +	 * from start of RAM. +	 */ +ENTRY(efi_stub_entry) +	/* +	 * Create a stack frame to save FP/LR with extra space +	 * for image_addr variable passed to efi_entry(). +	 */ +	stp	x29, x30, [sp, #-32]! + +	/* +	 * Call efi_entry to do the real work. +	 * x0 and x1 are already set up by firmware. Current runtime +	 * address of image is calculated and passed via *image_addr. +	 * +	 * unsigned long efi_entry(void *handle, +	 *                         efi_system_table_t *sys_table, +	 *                         unsigned long *image_addr) ; +	 */ +	adrp	x8, _text +	add	x8, x8, #:lo12:_text +	add	x2, sp, 16 +	str	x8, [x2] +	bl	efi_entry +	cmn	x0, #1 +	b.eq	efi_load_fail + +	/* +	 * efi_entry() will have relocated the kernel image if necessary +	 * and we return here with device tree address in x0 and the kernel +	 * entry point stored at *image_addr. Save those values in registers +	 * which are callee preserved. +	 */ +	mov	x20, x0		// DTB address +	ldr	x0, [sp, #16]	// relocated _text address +	mov	x21, x0 + +	/* +	 * Flush dcache covering current runtime addresses +	 * of kernel text/data. Then flush all of icache. +	 */ +	adrp	x1, _text +	add	x1, x1, #:lo12:_text +	adrp	x2, _edata +	add	x2, x2, #:lo12:_edata +	sub	x1, x2, x1 + +	bl	__flush_dcache_area +	ic	ialluis + +	/* Turn off Dcache and MMU */ +	mrs	x0, CurrentEL +	cmp	x0, #CurrentEL_EL2 +	b.ne	1f +	mrs	x0, sctlr_el2 +	bic	x0, x0, #1 << 0	// clear SCTLR.M +	bic	x0, x0, #1 << 2	// clear SCTLR.C +	msr	sctlr_el2, x0 +	isb +	b	2f +1: +	mrs	x0, sctlr_el1 +	bic	x0, x0, #1 << 0	// clear SCTLR.M +	bic	x0, x0, #1 << 2	// clear SCTLR.C +	msr	sctlr_el1, x0 +	isb +2: +	/* Jump to kernel entry point */ +	mov	x0, x20 +	mov	x1, xzr +	mov	x2, xzr +	mov	x3, xzr +	br	x21 + +efi_load_fail: +	mov	x0, #EFI_LOAD_ERROR +	ldp	x29, x30, [sp], #32 +	ret + +ENDPROC(efi_stub_entry) diff --git a/arch/arm64/kernel/efi-stub.c b/arch/arm64/kernel/efi-stub.c new file mode 100644 index 00000000000..e786e6cdc40 --- /dev/null +++ b/arch/arm64/kernel/efi-stub.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013, 2014 Linaro Ltd;  <roy.franz@linaro.org> + * + * This file implements the EFI boot stub for the arm64 kernel. + * Adapted from ARM version by Mark Salter <msalter@redhat.com> + * + * 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/efi.h> +#include <linux/libfdt.h> +#include <asm/sections.h> + +/* + * AArch64 requires the DTB to be 8-byte aligned in the first 512MiB from + * start of kernel and may not cross a 2MiB boundary. We set alignment to + * 2MiB so we know it won't cross a 2MiB boundary. + */ +#define EFI_FDT_ALIGN	SZ_2M   /* used by allocate_new_fdt_and_exit_boot() */ +#define MAX_FDT_OFFSET	SZ_512M + +#define efi_call_early(f, ...) sys_table_arg->boottime->f(__VA_ARGS__) + +static void efi_char16_printk(efi_system_table_t *sys_table_arg, +			      efi_char16_t *str); + +static efi_status_t efi_open_volume(efi_system_table_t *sys_table, +				    void *__image, void **__fh); +static efi_status_t efi_file_close(void *handle); + +static efi_status_t +efi_file_read(void *handle, unsigned long *size, void *addr); + +static efi_status_t +efi_file_size(efi_system_table_t *sys_table, void *__fh, +	      efi_char16_t *filename_16, void **handle, u64 *file_sz); + +/* Include shared EFI stub code */ +#include "../../../drivers/firmware/efi/efi-stub-helper.c" +#include "../../../drivers/firmware/efi/fdt.c" +#include "../../../drivers/firmware/efi/arm-stub.c" + + +static efi_status_t handle_kernel_image(efi_system_table_t *sys_table, +					unsigned long *image_addr, +					unsigned long *image_size, +					unsigned long *reserve_addr, +					unsigned long *reserve_size, +					unsigned long dram_base, +					efi_loaded_image_t *image) +{ +	efi_status_t status; +	unsigned long kernel_size, kernel_memsize = 0; + +	/* Relocate the image, if required. */ +	kernel_size = _edata - _text; +	if (*image_addr != (dram_base + TEXT_OFFSET)) { +		kernel_memsize = kernel_size + (_end - _edata); +		status = efi_relocate_kernel(sys_table, image_addr, +					     kernel_size, kernel_memsize, +					     dram_base + TEXT_OFFSET, +					     PAGE_SIZE); +		if (status != EFI_SUCCESS) { +			pr_efi_err(sys_table, "Failed to relocate kernel\n"); +			return status; +		} +		if (*image_addr != (dram_base + TEXT_OFFSET)) { +			pr_efi_err(sys_table, "Failed to alloc kernel memory\n"); +			efi_free(sys_table, kernel_memsize, *image_addr); +			return EFI_ERROR; +		} +		*image_size = kernel_memsize; +	} + + +	return EFI_SUCCESS; +} diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c new file mode 100644 index 00000000000..14db1f6e8d7 --- /dev/null +++ b/arch/arm64/kernel/efi.c @@ -0,0 +1,469 @@ +/* + * Extensible Firmware Interface + * + * Based on Extensible Firmware Interface Specification version 2.4 + * + * Copyright (C) 2013, 2014 Linaro 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. + * + */ + +#include <linux/efi.h> +#include <linux/export.h> +#include <linux/memblock.h> +#include <linux/bootmem.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/sched.h> +#include <linux/slab.h> + +#include <asm/cacheflush.h> +#include <asm/efi.h> +#include <asm/tlbflush.h> +#include <asm/mmu_context.h> + +struct efi_memory_map memmap; + +static efi_runtime_services_t *runtime; + +static u64 efi_system_table; + +static int uefi_debug __initdata; +static int __init uefi_debug_setup(char *str) +{ +	uefi_debug = 1; + +	return 0; +} +early_param("uefi_debug", uefi_debug_setup); + +static int __init is_normal_ram(efi_memory_desc_t *md) +{ +	if (md->attribute & EFI_MEMORY_WB) +		return 1; +	return 0; +} + +static void __init efi_setup_idmap(void) +{ +	struct memblock_region *r; +	efi_memory_desc_t *md; +	u64 paddr, npages, size; + +	for_each_memblock(memory, r) +		create_id_mapping(r->base, r->size, 0); + +	/* map runtime io spaces */ +	for_each_efi_memory_desc(&memmap, md) { +		if (!(md->attribute & EFI_MEMORY_RUNTIME) || is_normal_ram(md)) +			continue; +		paddr = md->phys_addr; +		npages = md->num_pages; +		memrange_efi_to_native(&paddr, &npages); +		size = npages << PAGE_SHIFT; +		create_id_mapping(paddr, size, 1); +	} +} + +static int __init uefi_init(void) +{ +	efi_char16_t *c16; +	char vendor[100] = "unknown"; +	int i, retval; + +	efi.systab = early_memremap(efi_system_table, +				    sizeof(efi_system_table_t)); +	if (efi.systab == NULL) { +		pr_warn("Unable to map EFI system table.\n"); +		return -ENOMEM; +	} + +	set_bit(EFI_BOOT, &efi.flags); +	set_bit(EFI_64BIT, &efi.flags); + +	/* +	 * Verify the EFI Table +	 */ +	if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { +		pr_err("System table signature incorrect\n"); +		return -EINVAL; +	} +	if ((efi.systab->hdr.revision >> 16) < 2) +		pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n", +			efi.systab->hdr.revision >> 16, +			efi.systab->hdr.revision & 0xffff); + +	/* Show what we know for posterity */ +	c16 = early_memremap(efi.systab->fw_vendor, +			     sizeof(vendor)); +	if (c16) { +		for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i) +			vendor[i] = c16[i]; +		vendor[i] = '\0'; +	} + +	pr_info("EFI v%u.%.02u by %s\n", +		efi.systab->hdr.revision >> 16, +		efi.systab->hdr.revision & 0xffff, vendor); + +	retval = efi_config_init(NULL); +	if (retval == 0) +		set_bit(EFI_CONFIG_TABLES, &efi.flags); + +	early_memunmap(c16, sizeof(vendor)); +	early_memunmap(efi.systab,  sizeof(efi_system_table_t)); + +	return retval; +} + +static __initdata char memory_type_name[][32] = { +	{"Reserved"}, +	{"Loader Code"}, +	{"Loader Data"}, +	{"Boot Code"}, +	{"Boot Data"}, +	{"Runtime Code"}, +	{"Runtime Data"}, +	{"Conventional Memory"}, +	{"Unusable Memory"}, +	{"ACPI Reclaim Memory"}, +	{"ACPI Memory NVS"}, +	{"Memory Mapped I/O"}, +	{"MMIO Port Space"}, +	{"PAL Code"}, +}; + +/* + * Return true for RAM regions we want to permanently reserve. + */ +static __init int is_reserve_region(efi_memory_desc_t *md) +{ +	if (!is_normal_ram(md)) +		return 0; + +	if (md->attribute & EFI_MEMORY_RUNTIME) +		return 1; + +	if (md->type == EFI_ACPI_RECLAIM_MEMORY || +	    md->type == EFI_RESERVED_TYPE) +		return 1; + +	return 0; +} + +static __init void reserve_regions(void) +{ +	efi_memory_desc_t *md; +	u64 paddr, npages, size; + +	if (uefi_debug) +		pr_info("Processing EFI memory map:\n"); + +	for_each_efi_memory_desc(&memmap, md) { +		paddr = md->phys_addr; +		npages = md->num_pages; + +		if (uefi_debug) +			pr_info("  0x%012llx-0x%012llx [%s]", +				paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1, +				memory_type_name[md->type]); + +		memrange_efi_to_native(&paddr, &npages); +		size = npages << PAGE_SHIFT; + +		if (is_normal_ram(md)) +			early_init_dt_add_memory_arch(paddr, size); + +		if (is_reserve_region(md) || +		    md->type == EFI_BOOT_SERVICES_CODE || +		    md->type == EFI_BOOT_SERVICES_DATA) { +			memblock_reserve(paddr, size); +			if (uefi_debug) +				pr_cont("*"); +		} + +		if (uefi_debug) +			pr_cont("\n"); +	} +} + + +static u64 __init free_one_region(u64 start, u64 end) +{ +	u64 size = end - start; + +	if (uefi_debug) +		pr_info("  EFI freeing: 0x%012llx-0x%012llx\n",	start, end - 1); + +	free_bootmem_late(start, size); +	return size; +} + +static u64 __init free_region(u64 start, u64 end) +{ +	u64 map_start, map_end, total = 0; + +	if (end <= start) +		return total; + +	map_start = (u64)memmap.phys_map; +	map_end = PAGE_ALIGN(map_start + (memmap.map_end - memmap.map)); +	map_start &= PAGE_MASK; + +	if (start < map_end && end > map_start) { +		/* region overlaps UEFI memmap */ +		if (start < map_start) +			total += free_one_region(start, map_start); + +		if (map_end < end) +			total += free_one_region(map_end, end); +	} else +		total += free_one_region(start, end); + +	return total; +} + +static void __init free_boot_services(void) +{ +	u64 total_freed = 0; +	u64 keep_end, free_start, free_end; +	efi_memory_desc_t *md; + +	/* +	 * If kernel uses larger pages than UEFI, we have to be careful +	 * not to inadvertantly free memory we want to keep if there is +	 * overlap at the kernel page size alignment. We do not want to +	 * free is_reserve_region() memory nor the UEFI memmap itself. +	 * +	 * The memory map is sorted, so we keep track of the end of +	 * any previous region we want to keep, remember any region +	 * we want to free and defer freeing it until we encounter +	 * the next region we want to keep. This way, before freeing +	 * it, we can clip it as needed to avoid freeing memory we +	 * want to keep for UEFI. +	 */ + +	keep_end = 0; +	free_start = 0; + +	for_each_efi_memory_desc(&memmap, md) { +		u64 paddr, npages, size; + +		if (is_reserve_region(md)) { +			/* +			 * We don't want to free any memory from this region. +			 */ +			if (free_start) { +				/* adjust free_end then free region */ +				if (free_end > md->phys_addr) +					free_end -= PAGE_SIZE; +				total_freed += free_region(free_start, free_end); +				free_start = 0; +			} +			keep_end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT); +			continue; +		} + +		if (md->type != EFI_BOOT_SERVICES_CODE && +		    md->type != EFI_BOOT_SERVICES_DATA) { +			/* no need to free this region */ +			continue; +		} + +		/* +		 * We want to free memory from this region. +		 */ +		paddr = md->phys_addr; +		npages = md->num_pages; +		memrange_efi_to_native(&paddr, &npages); +		size = npages << PAGE_SHIFT; + +		if (free_start) { +			if (paddr <= free_end) +				free_end = paddr + size; +			else { +				total_freed += free_region(free_start, free_end); +				free_start = paddr; +				free_end = paddr + size; +			} +		} else { +			free_start = paddr; +			free_end = paddr + size; +		} +		if (free_start < keep_end) { +			free_start += PAGE_SIZE; +			if (free_start >= free_end) +				free_start = 0; +		} +	} +	if (free_start) +		total_freed += free_region(free_start, free_end); + +	if (total_freed) +		pr_info("Freed 0x%llx bytes of EFI boot services memory", +			total_freed); +} + +void __init efi_init(void) +{ +	struct efi_fdt_params params; + +	/* Grab UEFI information placed in FDT by stub */ +	if (!efi_get_fdt_params(¶ms, uefi_debug)) +		return; + +	efi_system_table = params.system_table; + +	memblock_reserve(params.mmap & PAGE_MASK, +			 PAGE_ALIGN(params.mmap_size + (params.mmap & ~PAGE_MASK))); +	memmap.phys_map = (void *)params.mmap; +	memmap.map = early_memremap(params.mmap, params.mmap_size); +	memmap.map_end = memmap.map + params.mmap_size; +	memmap.desc_size = params.desc_size; +	memmap.desc_version = params.desc_ver; + +	if (uefi_init() < 0) +		return; + +	reserve_regions(); +} + +void __init efi_idmap_init(void) +{ +	if (!efi_enabled(EFI_BOOT)) +		return; + +	/* boot time idmap_pg_dir is incomplete, so fill in missing parts */ +	efi_setup_idmap(); +} + +static int __init remap_region(efi_memory_desc_t *md, void **new) +{ +	u64 paddr, vaddr, npages, size; + +	paddr = md->phys_addr; +	npages = md->num_pages; +	memrange_efi_to_native(&paddr, &npages); +	size = npages << PAGE_SHIFT; + +	if (is_normal_ram(md)) +		vaddr = (__force u64)ioremap_cache(paddr, size); +	else +		vaddr = (__force u64)ioremap(paddr, size); + +	if (!vaddr) { +		pr_err("Unable to remap 0x%llx pages @ %p\n", +		       npages, (void *)paddr); +		return 0; +	} + +	/* adjust for any rounding when EFI and system pagesize differs */ +	md->virt_addr = vaddr + (md->phys_addr - paddr); + +	if (uefi_debug) +		pr_info("  EFI remap 0x%012llx => %p\n", +			md->phys_addr, (void *)md->virt_addr); + +	memcpy(*new, md, memmap.desc_size); +	*new += memmap.desc_size; + +	return 1; +} + +/* + * Switch UEFI from an identity map to a kernel virtual map + */ +static int __init arm64_enter_virtual_mode(void) +{ +	efi_memory_desc_t *md; +	phys_addr_t virtmap_phys; +	void *virtmap, *virt_md; +	efi_status_t status; +	u64 mapsize; +	int count = 0; +	unsigned long flags; + +	if (!efi_enabled(EFI_BOOT)) { +		pr_info("EFI services will not be available.\n"); +		return -1; +	} + +	pr_info("Remapping and enabling EFI services.\n"); + +	/* replace early memmap mapping with permanent mapping */ +	mapsize = memmap.map_end - memmap.map; +	early_memunmap(memmap.map, mapsize); +	memmap.map = (__force void *)ioremap_cache((phys_addr_t)memmap.phys_map, +						   mapsize); +	memmap.map_end = memmap.map + mapsize; + +	efi.memmap = &memmap; + +	/* Map the runtime regions */ +	virtmap = kmalloc(mapsize, GFP_KERNEL); +	if (!virtmap) { +		pr_err("Failed to allocate EFI virtual memmap\n"); +		return -1; +	} +	virtmap_phys = virt_to_phys(virtmap); +	virt_md = virtmap; + +	for_each_efi_memory_desc(&memmap, md) { +		if (!(md->attribute & EFI_MEMORY_RUNTIME)) +			continue; +		if (remap_region(md, &virt_md)) +			++count; +	} + +	efi.systab = (__force void *)efi_lookup_mapped_addr(efi_system_table); +	if (efi.systab) +		set_bit(EFI_SYSTEM_TABLES, &efi.flags); + +	local_irq_save(flags); +	cpu_switch_mm(idmap_pg_dir, &init_mm); + +	/* Call SetVirtualAddressMap with the physical address of the map */ +	runtime = efi.systab->runtime; +	efi.set_virtual_address_map = runtime->set_virtual_address_map; + +	status = efi.set_virtual_address_map(count * memmap.desc_size, +					     memmap.desc_size, +					     memmap.desc_version, +					     (efi_memory_desc_t *)virtmap_phys); +	cpu_set_reserved_ttbr0(); +	flush_tlb_all(); +	local_irq_restore(flags); + +	kfree(virtmap); + +	free_boot_services(); + +	if (status != EFI_SUCCESS) { +		pr_err("Failed to set EFI virtual address map! [%lx]\n", +			status); +		return -1; +	} + +	/* Set up runtime services function pointers */ +	runtime = efi.systab->runtime; +	efi.get_time = runtime->get_time; +	efi.set_time = runtime->set_time; +	efi.get_wakeup_time = runtime->get_wakeup_time; +	efi.set_wakeup_time = runtime->set_wakeup_time; +	efi.get_variable = runtime->get_variable; +	efi.get_next_variable = runtime->get_next_variable; +	efi.set_variable = runtime->set_variable; +	efi.query_variable_info = runtime->query_variable_info; +	efi.update_capsule = runtime->update_capsule; +	efi.query_capsule_caps = runtime->query_capsule_caps; +	efi.get_next_high_mono_count = runtime->get_next_high_mono_count; +	efi.reset_system = runtime->reset_system; + +	set_bit(EFI_RUNTIME_SERVICES, &efi.flags); + +	return 0; +} +early_initcall(arm64_enter_virtual_mode); diff --git a/arch/arm64/kernel/entry-fpsimd.S b/arch/arm64/kernel/entry-fpsimd.S index 6a27cd6dbfa..d358ccacfc0 100644 --- a/arch/arm64/kernel/entry-fpsimd.S +++ b/arch/arm64/kernel/entry-fpsimd.S @@ -41,3 +41,27 @@ ENTRY(fpsimd_load_state)  	fpsimd_restore x0, 8  	ret  ENDPROC(fpsimd_load_state) + +#ifdef CONFIG_KERNEL_MODE_NEON + +/* + * Save the bottom n FP registers. + * + * x0 - pointer to struct fpsimd_partial_state + */ +ENTRY(fpsimd_save_partial_state) +	fpsimd_save_partial x0, 1, 8, 9 +	ret +ENDPROC(fpsimd_load_partial_state) + +/* + * Load the bottom n FP registers. + * + * x0 - pointer to struct fpsimd_partial_state + */ +ENTRY(fpsimd_load_partial_state) +	fpsimd_restore_partial x0, 8, 9 +	ret +ENDPROC(fpsimd_load_partial_state) + +#endif diff --git a/arch/arm64/kernel/entry-ftrace.S b/arch/arm64/kernel/entry-ftrace.S new file mode 100644 index 00000000000..aa5f9fcbf9e --- /dev/null +++ b/arch/arm64/kernel/entry-ftrace.S @@ -0,0 +1,218 @@ +/* + * arch/arm64/kernel/entry-ftrace.S + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro <takahiro.akashi@linaro.org> + * + * 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/linkage.h> +#include <asm/ftrace.h> +#include <asm/insn.h> + +/* + * Gcc with -pg will put the following code in the beginning of each function: + *      mov x0, x30 + *      bl _mcount + *	[function's body ...] + * "bl _mcount" may be replaced to "bl ftrace_caller" or NOP if dynamic + * ftrace is enabled. + * + * Please note that x0 as an argument will not be used here because we can + * get lr(x30) of instrumented function at any time by winding up call stack + * as long as the kernel is compiled without -fomit-frame-pointer. + * (or CONFIG_FRAME_POINTER, this is forced on arm64) + * + * stack layout after mcount_enter in _mcount(): + * + * current sp/fp =>  0:+-----+ + * in _mcount()        | x29 | -> instrumented function's fp + *                     +-----+ + *                     | x30 | -> _mcount()'s lr (= instrumented function's pc) + * old sp       => +16:+-----+ + * when instrumented   |     | + * function calls      | ... | + * _mcount()           |     | + *                     |     | + * instrumented => +xx:+-----+ + * function's fp       | x29 | -> parent's fp + *                     +-----+ + *                     | x30 | -> instrumented function's lr (= parent's pc) + *                     +-----+ + *                     | ... | + */ + +	.macro mcount_enter +	stp	x29, x30, [sp, #-16]! +	mov	x29, sp +	.endm + +	.macro mcount_exit +	ldp	x29, x30, [sp], #16 +	ret +	.endm + +	.macro mcount_adjust_addr rd, rn +	sub	\rd, \rn, #AARCH64_INSN_SIZE +	.endm + +	/* for instrumented function's parent */ +	.macro mcount_get_parent_fp reg +	ldr	\reg, [x29] +	ldr	\reg, [\reg] +	.endm + +	/* for instrumented function */ +	.macro mcount_get_pc0 reg +	mcount_adjust_addr	\reg, x30 +	.endm + +	.macro mcount_get_pc reg +	ldr	\reg, [x29, #8] +	mcount_adjust_addr	\reg, \reg +	.endm + +	.macro mcount_get_lr reg +	ldr	\reg, [x29] +	ldr	\reg, [\reg, #8] +	mcount_adjust_addr	\reg, \reg +	.endm + +	.macro mcount_get_lr_addr reg +	ldr	\reg, [x29] +	add	\reg, \reg, #8 +	.endm + +#ifndef CONFIG_DYNAMIC_FTRACE +/* + * void _mcount(unsigned long return_address) + * @return_address: return address to instrumented function + * + * This function makes calls, if enabled, to: + *     - tracer function to probe instrumented function's entry, + *     - ftrace_graph_caller to set up an exit hook + */ +ENTRY(_mcount) +#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST +	ldr	x0, =ftrace_trace_stop +	ldr	x0, [x0]		// if ftrace_trace_stop +	ret				//   return; +#endif +	mcount_enter + +	ldr	x0, =ftrace_trace_function +	ldr	x2, [x0] +	adr	x0, ftrace_stub +	cmp	x0, x2			// if (ftrace_trace_function +	b.eq	skip_ftrace_call	//     != ftrace_stub) { + +	mcount_get_pc	x0		//       function's pc +	mcount_get_lr	x1		//       function's lr (= parent's pc) +	blr	x2			//   (*ftrace_trace_function)(pc, lr); + +#ifndef CONFIG_FUNCTION_GRAPH_TRACER +skip_ftrace_call:			//   return; +	mcount_exit			// } +#else +	mcount_exit			//   return; +					// } +skip_ftrace_call: +	ldr	x1, =ftrace_graph_return +	ldr	x2, [x1]		//   if ((ftrace_graph_return +	cmp	x0, x2			//        != ftrace_stub) +	b.ne	ftrace_graph_caller + +	ldr	x1, =ftrace_graph_entry	//     || (ftrace_graph_entry +	ldr	x2, [x1]		//        != ftrace_graph_entry_stub)) +	ldr	x0, =ftrace_graph_entry_stub +	cmp	x0, x2 +	b.ne	ftrace_graph_caller	//     ftrace_graph_caller(); + +	mcount_exit +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ +ENDPROC(_mcount) + +#else /* CONFIG_DYNAMIC_FTRACE */ +/* + * _mcount() is used to build the kernel with -pg option, but all the branch + * instructions to _mcount() are replaced to NOP initially at kernel start up, + * and later on, NOP to branch to ftrace_caller() when enabled or branch to + * NOP when disabled per-function base. + */ +ENTRY(_mcount) +	ret +ENDPROC(_mcount) + +/* + * void ftrace_caller(unsigned long return_address) + * @return_address: return address to instrumented function + * + * This function is a counterpart of _mcount() in 'static' ftrace, and + * makes calls to: + *     - tracer function to probe instrumented function's entry, + *     - ftrace_graph_caller to set up an exit hook + */ +ENTRY(ftrace_caller) +	mcount_enter + +	mcount_get_pc0	x0		//     function's pc +	mcount_get_lr	x1		//     function's lr + +	.global ftrace_call +ftrace_call:				// tracer(pc, lr); +	nop				// This will be replaced with "bl xxx" +					// where xxx can be any kind of tracer. + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +	.global ftrace_graph_call +ftrace_graph_call:			// ftrace_graph_caller(); +	nop				// If enabled, this will be replaced +					// "b ftrace_graph_caller" +#endif + +	mcount_exit +ENDPROC(ftrace_caller) +#endif /* CONFIG_DYNAMIC_FTRACE */ + +ENTRY(ftrace_stub) +	ret +ENDPROC(ftrace_stub) + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/* + * void ftrace_graph_caller(void) + * + * Called from _mcount() or ftrace_caller() when function_graph tracer is + * selected. + * This function w/ prepare_ftrace_return() fakes link register's value on + * the call stack in order to intercept instrumented function's return path + * and run return_to_handler() later on its exit. + */ +ENTRY(ftrace_graph_caller) +	mcount_get_lr_addr	  x0	//     pointer to function's saved lr +	mcount_get_pc		  x1	//     function's pc +	mcount_get_parent_fp	  x2	//     parent's fp +	bl	prepare_ftrace_return	// prepare_ftrace_return(&lr, pc, fp) + +	mcount_exit +ENDPROC(ftrace_graph_caller) + +/* + * void return_to_handler(void) + * + * Run ftrace_return_to_handler() before going back to parent. + * @fp is checked against the value passed by ftrace_graph_caller() + * only when CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST is enabled. + */ +ENTRY(return_to_handler) +	str	x0, [sp, #-16]! +	mov	x0, x29			//     parent's fp +	bl	ftrace_return_to_handler// addr = ftrace_return_to_hander(fp); +	mov	x30, x0			// restore the original return address +	ldr	x0, [sp], #16 +	ret +END(return_to_handler) +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 3881fd115eb..9ce04ba6bcb 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -60,6 +60,9 @@  	push	x0, x1  	.if	\el == 0  	mrs	x21, sp_el0 +	get_thread_info tsk			// Ensure MDSCR_EL1.SS is clear, +	ldr	x19, [tsk, #TI_FLAGS]		// since we can unmask debug +	disable_step_tsk x19, x20		// exceptions when scheduling.  	.else  	add	x21, sp, #S_FRAME_SIZE  	.endif @@ -259,7 +262,7 @@ el1_da:  	 * Data abort handling  	 */  	mrs	x0, far_el1 -	enable_dbg_if_not_stepping x2 +	enable_dbg  	// re-enable interrupts if they were enabled in the aborted context  	tbnz	x23, #7, 1f			// PSR_I_BIT  	enable_irq @@ -275,27 +278,31 @@ el1_sp_pc:  	 * Stack or PC alignment exception handling  	 */  	mrs	x0, far_el1 -	mov	x1, x25 +	enable_dbg  	mov	x2, sp  	b	do_sp_pc_abort  el1_undef:  	/*  	 * Undefined instruction  	 */ +	enable_dbg  	mov	x0, sp  	b	do_undefinstr  el1_dbg:  	/*  	 * Debug exception handling  	 */ +	cmp	x24, #ESR_EL1_EC_BRK64		// if BRK64 +	cinc	x24, x24, eq			// set bit '0'  	tbz	x24, #0, el1_inv		// EL1 only  	mrs	x0, far_el1  	mov	x2, sp				// struct pt_regs  	bl	do_debug_exception - +	enable_dbg  	kernel_exit 1  el1_inv:  	// TODO: add support for undefined instructions in kernel mode +	enable_dbg  	mov	x0, sp  	mov	x1, #BAD_SYNC  	mrs	x2, esr_el1 @@ -305,20 +312,17 @@ ENDPROC(el1_sync)  	.align	6  el1_irq:  	kernel_entry 1 -	enable_dbg_if_not_stepping x0 +	enable_dbg  #ifdef CONFIG_TRACE_IRQFLAGS  	bl	trace_hardirqs_off  #endif -#ifdef CONFIG_PREEMPT -	get_thread_info tsk -	ldr	x24, [tsk, #TI_PREEMPT]		// get preempt count -	add	x0, x24, #1			// increment it -	str	x0, [tsk, #TI_PREEMPT] -#endif +  	irq_handler +  #ifdef CONFIG_PREEMPT -	str	x24, [tsk, #TI_PREEMPT]		// restore preempt count -	cbnz	x24, 1f				// preempt count != 0 +	get_thread_info tsk +	ldr	w24, [tsk, #TI_PREEMPT]		// get preempt count +	cbnz	w24, 1f				// preempt count != 0  	ldr	x0, [tsk, #TI_FLAGS]		// get flags  	tbz	x0, #TIF_NEED_RESCHED, 1f	// needs rescheduling?  	bl	el1_preempt @@ -333,8 +337,7 @@ ENDPROC(el1_irq)  #ifdef CONFIG_PREEMPT  el1_preempt:  	mov	x24, lr -1:	enable_dbg -	bl	preempt_schedule_irq		// irq en/disable is done inside +1:	bl	preempt_schedule_irq		// irq en/disable is done inside  	ldr	x0, [tsk, #TI_FLAGS]		// get new tasks TI_FLAGS  	tbnz	x0, #TIF_NEED_RESCHED, 1b	// needs rescheduling?  	ret	x24 @@ -350,7 +353,7 @@ el0_sync:  	lsr	x24, x25, #ESR_EL1_EC_SHIFT	// exception class  	cmp	x24, #ESR_EL1_EC_SVC64		// SVC in 64-bit state  	b.eq	el0_svc -	adr	lr, ret_from_exception +	adr	lr, ret_to_user  	cmp	x24, #ESR_EL1_EC_DABT_EL0	// data abort in EL0  	b.eq	el0_da  	cmp	x24, #ESR_EL1_EC_IABT_EL0	// instruction abort in EL0 @@ -379,7 +382,7 @@ el0_sync_compat:  	lsr	x24, x25, #ESR_EL1_EC_SHIFT	// exception class  	cmp	x24, #ESR_EL1_EC_SVC32		// SVC in 32-bit state  	b.eq	el0_svc_compat -	adr	lr, ret_from_exception +	adr	lr, ret_to_user  	cmp	x24, #ESR_EL1_EC_DABT_EL0	// data abort in EL0  	b.eq	el0_da  	cmp	x24, #ESR_EL1_EC_IABT_EL0	// instruction abort in EL0 @@ -424,11 +427,8 @@ el0_da:  	 */  	mrs	x0, far_el1  	bic	x0, x0, #(0xff << 56) -	disable_step x1 -	isb -	enable_dbg  	// enable interrupts before calling the main handler -	enable_irq +	enable_dbg_and_irq  	mov	x1, x25  	mov	x2, sp  	b	do_mem_abort @@ -437,11 +437,8 @@ el0_ia:  	 * Instruction abort handling  	 */  	mrs	x0, far_el1 -	disable_step x1 -	isb -	enable_dbg  	// enable interrupts before calling the main handler -	enable_irq +	enable_dbg_and_irq  	orr	x1, x25, #1 << 24		// use reserved ISS bit for instruction aborts  	mov	x2, sp  	b	do_mem_abort @@ -449,6 +446,7 @@ el0_fpsimd_acc:  	/*  	 * Floating Point or Advanced SIMD access  	 */ +	enable_dbg  	mov	x0, x25  	mov	x1, sp  	b	do_fpsimd_acc @@ -456,6 +454,7 @@ el0_fpsimd_exc:  	/*  	 * Floating Point or Advanced SIMD exception  	 */ +	enable_dbg  	mov	x0, x25  	mov	x1, sp  	b	do_fpsimd_exc @@ -464,11 +463,8 @@ el0_sp_pc:  	 * Stack or PC alignment exception handling  	 */  	mrs	x0, far_el1 -	disable_step x1 -	isb -	enable_dbg  	// enable interrupts before calling the main handler -	enable_irq +	enable_dbg_and_irq  	mov	x1, x25  	mov	x2, sp  	b	do_sp_pc_abort @@ -476,9 +472,9 @@ el0_undef:  	/*  	 * Undefined instruction  	 */ -	mov	x0, sp  	// enable interrupts before calling the main handler -	enable_irq +	enable_dbg_and_irq +	mov	x0, sp  	b	do_undefinstr  el0_dbg:  	/* @@ -486,11 +482,13 @@ el0_dbg:  	 */  	tbnz	x24, #0, el0_inv		// EL0 only  	mrs	x0, far_el1 -	disable_step x1  	mov	x1, x25  	mov	x2, sp -	b	do_debug_exception +	bl	do_debug_exception +	enable_dbg +	b	ret_to_user  el0_inv: +	enable_dbg  	mov	x0, sp  	mov	x1, #BAD_SYNC  	mrs	x2, esr_el1 @@ -501,28 +499,13 @@ ENDPROC(el0_sync)  el0_irq:  	kernel_entry 0  el0_irq_naked: -	disable_step x1 -	isb  	enable_dbg  #ifdef CONFIG_TRACE_IRQFLAGS  	bl	trace_hardirqs_off  #endif -	get_thread_info tsk -#ifdef CONFIG_PREEMPT -	ldr	x24, [tsk, #TI_PREEMPT]		// get preempt count -	add	x23, x24, #1			// increment it -	str	x23, [tsk, #TI_PREEMPT] -#endif +  	irq_handler -#ifdef CONFIG_PREEMPT -	ldr	x0, [tsk, #TI_PREEMPT] -	str	x24, [tsk, #TI_PREEMPT] -	cmp	x0, x23 -	b.eq	1f -	mov	x1, #0 -	str	x1, [x1]			// BUG -1: -#endif +  #ifdef CONFIG_TRACE_IRQFLAGS  	bl	trace_hardirqs_on  #endif @@ -530,14 +513,6 @@ el0_irq_naked:  ENDPROC(el0_irq)  /* - * This is the return code to user mode for abort handlers - */ -ret_from_exception: -	get_thread_info tsk -	b	ret_to_user -ENDPROC(ret_from_exception) - -/*   * Register switch for AArch64. The callee-saved registers need to be saved   * and restored. On entry:   *   x0 = previous task_struct (must be preserved across the switch) @@ -576,10 +551,7 @@ ret_fast_syscall:  	ldr	x1, [tsk, #TI_FLAGS]  	and	x2, x1, #_TIF_WORK_MASK  	cbnz	x2, fast_work_pending -	tbz	x1, #TIF_SINGLESTEP, fast_exit -	disable_dbg -	enable_step x2 -fast_exit: +	enable_step_tsk x1, x2  	kernel_exit 0, ret = 1  /* @@ -589,7 +561,7 @@ fast_work_pending:  	str	x0, [sp, #S_X0]			// returned x0  work_pending:  	tbnz	x1, #TIF_NEED_RESCHED, work_resched -	/* TIF_SIGPENDING or TIF_NOTIFY_RESUME case */ +	/* TIF_SIGPENDING, TIF_NOTIFY_RESUME or TIF_FOREIGN_FPSTATE case */  	ldr	x2, [sp, #S_PSTATE]  	mov	x0, sp				// 'regs'  	tst	x2, #PSR_MODE_MASK		// user mode regs? @@ -598,7 +570,6 @@ work_pending:  	bl	do_notify_resume  	b	ret_to_user  work_resched: -	enable_dbg  	bl	schedule  /* @@ -609,9 +580,7 @@ ret_to_user:  	ldr	x1, [tsk, #TI_FLAGS]  	and	x2, x1, #_TIF_WORK_MASK  	cbnz	x2, work_pending -	tbz	x1, #TIF_SINGLESTEP, no_work_pending -	disable_dbg -	enable_step x2 +	enable_step_tsk x1, x2  no_work_pending:  	kernel_exit 0, ret = 0  ENDPROC(ret_to_user) @@ -638,14 +607,11 @@ el0_svc:  	mov	sc_nr, #__NR_syscalls  el0_svc_naked:					// compat entry point  	stp	x0, scno, [sp, #S_ORIG_X0]	// save the original x0 and syscall number -	disable_step x16 -	isb -	enable_dbg -	enable_irq +	enable_dbg_and_irq -	get_thread_info tsk -	ldr	x16, [tsk, #TI_FLAGS]		// check for syscall tracing -	tbnz	x16, #TIF_SYSCALL_TRACE, __sys_trace // are we tracing syscalls? +	ldr	x16, [tsk, #TI_FLAGS]		// check for syscall hooks +	tst	x16, #_TIF_SYSCALL_WORK +	b.ne	__sys_trace  	adr	lr, ret_fast_syscall		// return address  	cmp     scno, sc_nr                     // check upper syscall limit  	b.hs	ni_sys @@ -661,9 +627,8 @@ ENDPROC(el0_svc)  	 * switches, and waiting for our parent to respond.  	 */  __sys_trace: -	mov	x1, sp -	mov	w0, #0				// trace entry -	bl	syscall_trace +	mov	x0, sp +	bl	syscall_trace_enter  	adr	lr, __sys_trace_return		// return address  	uxtw	scno, w0			// syscall number (possibly new)  	mov	x1, sp				// pointer to regs @@ -678,9 +643,8 @@ __sys_trace:  __sys_trace_return:  	str	x0, [sp]			// save returned x0 -	mov	x1, sp -	mov	w0, #1				// trace exit -	bl	syscall_trace +	mov	x0, sp +	bl	syscall_trace_exit  	b	ret_to_user  /* diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c index 1f2e4d5a5c0..ad8aebb1cde 100644 --- a/arch/arm64/kernel/fpsimd.c +++ b/arch/arm64/kernel/fpsimd.c @@ -17,6 +17,7 @@   * along with this program.  If not, see <http://www.gnu.org/licenses/>.   */ +#include <linux/cpu_pm.h>  #include <linux/kernel.h>  #include <linux/init.h>  #include <linux/sched.h> @@ -34,6 +35,60 @@  #define FPEXC_IDF	(1 << 7)  /* + * In order to reduce the number of times the FPSIMD state is needlessly saved + * and restored, we need to keep track of two things: + * (a) for each task, we need to remember which CPU was the last one to have + *     the task's FPSIMD state loaded into its FPSIMD registers; + * (b) for each CPU, we need to remember which task's userland FPSIMD state has + *     been loaded into its FPSIMD registers most recently, or whether it has + *     been used to perform kernel mode NEON in the meantime. + * + * For (a), we add a 'cpu' field to struct fpsimd_state, which gets updated to + * the id of the current CPU everytime the state is loaded onto a CPU. For (b), + * we add the per-cpu variable 'fpsimd_last_state' (below), which contains the + * address of the userland FPSIMD state of the task that was loaded onto the CPU + * the most recently, or NULL if kernel mode NEON has been performed after that. + * + * With this in place, we no longer have to restore the next FPSIMD state right + * when switching between tasks. Instead, we can defer this check to userland + * resume, at which time we verify whether the CPU's fpsimd_last_state and the + * task's fpsimd_state.cpu are still mutually in sync. If this is the case, we + * can omit the FPSIMD restore. + * + * As an optimization, we use the thread_info flag TIF_FOREIGN_FPSTATE to + * indicate whether or not the userland FPSIMD state of the current task is + * present in the registers. The flag is set unless the FPSIMD registers of this + * CPU currently contain the most recent userland FPSIMD state of the current + * task. + * + * For a certain task, the sequence may look something like this: + * - the task gets scheduled in; if both the task's fpsimd_state.cpu field + *   contains the id of the current CPU, and the CPU's fpsimd_last_state per-cpu + *   variable points to the task's fpsimd_state, the TIF_FOREIGN_FPSTATE flag is + *   cleared, otherwise it is set; + * + * - the task returns to userland; if TIF_FOREIGN_FPSTATE is set, the task's + *   userland FPSIMD state is copied from memory to the registers, the task's + *   fpsimd_state.cpu field is set to the id of the current CPU, the current + *   CPU's fpsimd_last_state pointer is set to this task's fpsimd_state and the + *   TIF_FOREIGN_FPSTATE flag is cleared; + * + * - the task executes an ordinary syscall; upon return to userland, the + *   TIF_FOREIGN_FPSTATE flag will still be cleared, so no FPSIMD state is + *   restored; + * + * - the task executes a syscall which executes some NEON instructions; this is + *   preceded by a call to kernel_neon_begin(), which copies the task's FPSIMD + *   register contents to memory, clears the fpsimd_last_state per-cpu variable + *   and sets the TIF_FOREIGN_FPSTATE flag; + * + * - the task gets preempted after kernel_neon_end() is called; as we have not + *   returned from the 2nd syscall yet, TIF_FOREIGN_FPSTATE is still set so + *   whatever is in the FPSIMD registers is not saved to memory, but discarded. + */ +static DEFINE_PER_CPU(struct fpsimd_state *, fpsimd_last_state); + +/*   * Trapped FP/ASIMD access.   */  void do_fpsimd_acc(unsigned int esr, struct pt_regs *regs) @@ -71,46 +126,175 @@ void do_fpsimd_exc(unsigned int esr, struct pt_regs *regs)  void fpsimd_thread_switch(struct task_struct *next)  { -	/* check if not kernel threads */ -	if (current->mm) +	/* +	 * Save the current FPSIMD state to memory, but only if whatever is in +	 * the registers is in fact the most recent userland FPSIMD state of +	 * 'current'. +	 */ +	if (current->mm && !test_thread_flag(TIF_FOREIGN_FPSTATE))  		fpsimd_save_state(¤t->thread.fpsimd_state); -	if (next->mm) -		fpsimd_load_state(&next->thread.fpsimd_state); + +	if (next->mm) { +		/* +		 * If we are switching to a task whose most recent userland +		 * FPSIMD state is already in the registers of *this* cpu, +		 * we can skip loading the state from memory. Otherwise, set +		 * the TIF_FOREIGN_FPSTATE flag so the state will be loaded +		 * upon the next return to userland. +		 */ +		struct fpsimd_state *st = &next->thread.fpsimd_state; + +		if (__this_cpu_read(fpsimd_last_state) == st +		    && st->cpu == smp_processor_id()) +			clear_ti_thread_flag(task_thread_info(next), +					     TIF_FOREIGN_FPSTATE); +		else +			set_ti_thread_flag(task_thread_info(next), +					   TIF_FOREIGN_FPSTATE); +	}  }  void fpsimd_flush_thread(void)  {  	memset(¤t->thread.fpsimd_state, 0, sizeof(struct fpsimd_state)); -	fpsimd_load_state(¤t->thread.fpsimd_state); +	set_thread_flag(TIF_FOREIGN_FPSTATE);  } -#ifdef CONFIG_KERNEL_MODE_NEON +/* + * Save the userland FPSIMD state of 'current' to memory, but only if the state + * currently held in the registers does in fact belong to 'current' + */ +void fpsimd_preserve_current_state(void) +{ +	preempt_disable(); +	if (!test_thread_flag(TIF_FOREIGN_FPSTATE)) +		fpsimd_save_state(¤t->thread.fpsimd_state); +	preempt_enable(); +}  /* - * Kernel-side NEON support functions + * Load the userland FPSIMD state of 'current' from memory, but only if the + * FPSIMD state already held in the registers is /not/ the most recent FPSIMD + * state of 'current'   */ -void kernel_neon_begin(void) +void fpsimd_restore_current_state(void)  { -	/* Avoid using the NEON in interrupt context */ -	BUG_ON(in_interrupt());  	preempt_disable(); +	if (test_and_clear_thread_flag(TIF_FOREIGN_FPSTATE)) { +		struct fpsimd_state *st = ¤t->thread.fpsimd_state; -	if (current->mm) -		fpsimd_save_state(¤t->thread.fpsimd_state); +		fpsimd_load_state(st); +		this_cpu_write(fpsimd_last_state, st); +		st->cpu = smp_processor_id(); +	} +	preempt_enable();  } -EXPORT_SYMBOL(kernel_neon_begin); -void kernel_neon_end(void) +/* + * Load an updated userland FPSIMD state for 'current' from memory and set the + * flag that indicates that the FPSIMD register contents are the most recent + * FPSIMD state of 'current' + */ +void fpsimd_update_current_state(struct fpsimd_state *state)  { -	if (current->mm) -		fpsimd_load_state(¤t->thread.fpsimd_state); +	preempt_disable(); +	fpsimd_load_state(state); +	if (test_and_clear_thread_flag(TIF_FOREIGN_FPSTATE)) { +		struct fpsimd_state *st = ¤t->thread.fpsimd_state; +		this_cpu_write(fpsimd_last_state, st); +		st->cpu = smp_processor_id(); +	}  	preempt_enable();  } + +/* + * Invalidate live CPU copies of task t's FPSIMD state + */ +void fpsimd_flush_task_state(struct task_struct *t) +{ +	t->thread.fpsimd_state.cpu = NR_CPUS; +} + +#ifdef CONFIG_KERNEL_MODE_NEON + +static DEFINE_PER_CPU(struct fpsimd_partial_state, hardirq_fpsimdstate); +static DEFINE_PER_CPU(struct fpsimd_partial_state, softirq_fpsimdstate); + +/* + * Kernel-side NEON support functions + */ +void kernel_neon_begin_partial(u32 num_regs) +{ +	if (in_interrupt()) { +		struct fpsimd_partial_state *s = this_cpu_ptr( +			in_irq() ? &hardirq_fpsimdstate : &softirq_fpsimdstate); + +		BUG_ON(num_regs > 32); +		fpsimd_save_partial_state(s, roundup(num_regs, 2)); +	} else { +		/* +		 * Save the userland FPSIMD state if we have one and if we +		 * haven't done so already. Clear fpsimd_last_state to indicate +		 * that there is no longer userland FPSIMD state in the +		 * registers. +		 */ +		preempt_disable(); +		if (current->mm && +		    !test_and_set_thread_flag(TIF_FOREIGN_FPSTATE)) +			fpsimd_save_state(¤t->thread.fpsimd_state); +		this_cpu_write(fpsimd_last_state, NULL); +	} +} +EXPORT_SYMBOL(kernel_neon_begin_partial); + +void kernel_neon_end(void) +{ +	if (in_interrupt()) { +		struct fpsimd_partial_state *s = this_cpu_ptr( +			in_irq() ? &hardirq_fpsimdstate : &softirq_fpsimdstate); +		fpsimd_load_partial_state(s); +	} else { +		preempt_enable(); +	} +}  EXPORT_SYMBOL(kernel_neon_end);  #endif /* CONFIG_KERNEL_MODE_NEON */ +#ifdef CONFIG_CPU_PM +static int fpsimd_cpu_pm_notifier(struct notifier_block *self, +				  unsigned long cmd, void *v) +{ +	switch (cmd) { +	case CPU_PM_ENTER: +		if (current->mm && !test_thread_flag(TIF_FOREIGN_FPSTATE)) +			fpsimd_save_state(¤t->thread.fpsimd_state); +		break; +	case CPU_PM_EXIT: +		if (current->mm) +			set_thread_flag(TIF_FOREIGN_FPSTATE); +		break; +	case CPU_PM_ENTER_FAILED: +	default: +		return NOTIFY_DONE; +	} +	return NOTIFY_OK; +} + +static struct notifier_block fpsimd_cpu_pm_notifier_block = { +	.notifier_call = fpsimd_cpu_pm_notifier, +}; + +static void fpsimd_pm_init(void) +{ +	cpu_pm_register_notifier(&fpsimd_cpu_pm_notifier_block); +} + +#else +static inline void fpsimd_pm_init(void) { } +#endif /* CONFIG_CPU_PM */ +  /*   * FP/SIMD support code initialisation.   */ @@ -129,6 +313,8 @@ static int __init fpsimd_init(void)  	else  		elf_hwcap |= HWCAP_ASIMD; +	fpsimd_pm_init(); +  	return 0;  }  late_initcall(fpsimd_init); diff --git a/arch/arm64/kernel/ftrace.c b/arch/arm64/kernel/ftrace.c new file mode 100644 index 00000000000..7924d73b647 --- /dev/null +++ b/arch/arm64/kernel/ftrace.c @@ -0,0 +1,176 @@ +/* + * arch/arm64/kernel/ftrace.c + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro <takahiro.akashi@linaro.org> + * + * 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/ftrace.h> +#include <linux/swab.h> +#include <linux/uaccess.h> + +#include <asm/cacheflush.h> +#include <asm/ftrace.h> +#include <asm/insn.h> + +#ifdef CONFIG_DYNAMIC_FTRACE +/* + * Replace a single instruction, which may be a branch or NOP. + * If @validate == true, a replaced instruction is checked against 'old'. + */ +static int ftrace_modify_code(unsigned long pc, u32 old, u32 new, +			      bool validate) +{ +	u32 replaced; + +	/* +	 * Note: +	 * Due to modules and __init, code can disappear and change, +	 * we need to protect against faulting as well as code changing. +	 * We do this by aarch64_insn_*() which use the probe_kernel_*(). +	 * +	 * No lock is held here because all the modifications are run +	 * through stop_machine(). +	 */ +	if (validate) { +		if (aarch64_insn_read((void *)pc, &replaced)) +			return -EFAULT; + +		if (replaced != old) +			return -EINVAL; +	} +	if (aarch64_insn_patch_text_nosync((void *)pc, new)) +		return -EPERM; + +	return 0; +} + +/* + * Replace tracer function in ftrace_caller() + */ +int ftrace_update_ftrace_func(ftrace_func_t func) +{ +	unsigned long pc; +	u32 new; + +	pc = (unsigned long)&ftrace_call; +	new = aarch64_insn_gen_branch_imm(pc, (unsigned long)func, true); + +	return ftrace_modify_code(pc, 0, new, false); +} + +/* + * Turn on the call to ftrace_caller() in instrumented function + */ +int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr) +{ +	unsigned long pc = rec->ip; +	u32 old, new; + +	old = aarch64_insn_gen_nop(); +	new = aarch64_insn_gen_branch_imm(pc, addr, true); + +	return ftrace_modify_code(pc, old, new, true); +} + +/* + * Turn off the call to ftrace_caller() in instrumented function + */ +int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, +		    unsigned long addr) +{ +	unsigned long pc = rec->ip; +	u32 old, new; + +	old = aarch64_insn_gen_branch_imm(pc, addr, true); +	new = aarch64_insn_gen_nop(); + +	return ftrace_modify_code(pc, old, new, true); +} + +int __init ftrace_dyn_arch_init(void) +{ +	return 0; +} +#endif /* CONFIG_DYNAMIC_FTRACE */ + +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +/* + * function_graph tracer expects ftrace_return_to_handler() to be called + * on the way back to parent. For this purpose, this function is called + * in _mcount() or ftrace_caller() to replace return address (*parent) on + * the call stack to return_to_handler. + * + * Note that @frame_pointer is used only for sanity check later. + */ +void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, +			   unsigned long frame_pointer) +{ +	unsigned long return_hooker = (unsigned long)&return_to_handler; +	unsigned long old; +	struct ftrace_graph_ent trace; +	int err; + +	if (unlikely(atomic_read(¤t->tracing_graph_pause))) +		return; + +	/* +	 * Note: +	 * No protection against faulting at *parent, which may be seen +	 * on other archs. It's unlikely on AArch64. +	 */ +	old = *parent; +	*parent = return_hooker; + +	trace.func = self_addr; +	trace.depth = current->curr_ret_stack + 1; + +	/* Only trace if the calling function expects to */ +	if (!ftrace_graph_entry(&trace)) { +		*parent = old; +		return; +	} + +	err = ftrace_push_return_trace(old, self_addr, &trace.depth, +				       frame_pointer); +	if (err == -EBUSY) { +		*parent = old; +		return; +	} +} + +#ifdef CONFIG_DYNAMIC_FTRACE +/* + * Turn on/off the call to ftrace_graph_caller() in ftrace_caller() + * depending on @enable. + */ +static int ftrace_modify_graph_caller(bool enable) +{ +	unsigned long pc = (unsigned long)&ftrace_graph_call; +	u32 branch, nop; + +	branch = aarch64_insn_gen_branch_imm(pc, +			(unsigned long)ftrace_graph_caller, false); +	nop = aarch64_insn_gen_nop(); + +	if (enable) +		return ftrace_modify_code(pc, nop, branch, true); +	else +		return ftrace_modify_code(pc, branch, nop, true); +} + +int ftrace_enable_ftrace_graph_caller(void) +{ +	return ftrace_modify_graph_caller(true); +} + +int ftrace_disable_ftrace_graph_caller(void) +{ +	return ftrace_modify_graph_caller(false); +} +#endif /* CONFIG_DYNAMIC_FTRACE */ +#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S index 7090c126797..a2c1195abb7 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S @@ -26,6 +26,7 @@  #include <asm/assembler.h>  #include <asm/ptrace.h>  #include <asm/asm-offsets.h> +#include <asm/cache.h>  #include <asm/cputype.h>  #include <asm/memory.h>  #include <asm/thread_info.h> @@ -107,8 +108,18 @@  	/*  	 * DO NOT MODIFY. Image header expected by Linux boot-loaders.  	 */ +#ifdef CONFIG_EFI +efi_head: +	/* +	 * This add instruction has no meaningful effect except that +	 * its opcode forms the magic "MZ" signature required by UEFI. +	 */ +	add	x13, x18, #0x16 +	b	stext +#else  	b	stext				// branch to kernel start, magic  	.long	0				// reserved +#endif  	.quad	TEXT_OFFSET			// Image load offset from start of RAM  	.quad	0				// reserved  	.quad	0				// reserved @@ -119,12 +130,115 @@  	.byte	0x52  	.byte	0x4d  	.byte	0x64 +#ifdef CONFIG_EFI +	.long	pe_header - efi_head		// Offset to the PE header. +#else  	.word	0				// reserved +#endif + +#ifdef CONFIG_EFI +	.align 3 +pe_header: +	.ascii	"PE" +	.short 	0 +coff_header: +	.short	0xaa64				// AArch64 +	.short	2				// nr_sections +	.long	0 				// TimeDateStamp +	.long	0				// PointerToSymbolTable +	.long	1				// NumberOfSymbols +	.short	section_table - optional_header	// SizeOfOptionalHeader +	.short	0x206				// Characteristics. +						// IMAGE_FILE_DEBUG_STRIPPED | +						// IMAGE_FILE_EXECUTABLE_IMAGE | +						// IMAGE_FILE_LINE_NUMS_STRIPPED +optional_header: +	.short	0x20b				// PE32+ format +	.byte	0x02				// MajorLinkerVersion +	.byte	0x14				// MinorLinkerVersion +	.long	_edata - stext			// SizeOfCode +	.long	0				// SizeOfInitializedData +	.long	0				// SizeOfUninitializedData +	.long	efi_stub_entry - efi_head	// AddressOfEntryPoint +	.long	stext - efi_head		// BaseOfCode + +extra_header_fields: +	.quad	0				// ImageBase +	.long	0x20				// SectionAlignment +	.long	0x8				// FileAlignment +	.short	0				// MajorOperatingSystemVersion +	.short	0				// MinorOperatingSystemVersion +	.short	0				// MajorImageVersion +	.short	0				// MinorImageVersion +	.short	0				// MajorSubsystemVersion +	.short	0				// MinorSubsystemVersion +	.long	0				// Win32VersionValue + +	.long	_edata - efi_head		// SizeOfImage + +	// Everything before the kernel image is considered part of the header +	.long	stext - efi_head		// SizeOfHeaders +	.long	0				// CheckSum +	.short	0xa				// Subsystem (EFI application) +	.short	0				// DllCharacteristics +	.quad	0				// SizeOfStackReserve +	.quad	0				// SizeOfStackCommit +	.quad	0				// SizeOfHeapReserve +	.quad	0				// SizeOfHeapCommit +	.long	0				// LoaderFlags +	.long	0x6				// NumberOfRvaAndSizes + +	.quad	0				// ExportTable +	.quad	0				// ImportTable +	.quad	0				// ResourceTable +	.quad	0				// ExceptionTable +	.quad	0				// CertificationTable +	.quad	0				// BaseRelocationTable + +	// Section table +section_table: + +	/* +	 * The EFI application loader requires a relocation section +	 * because EFI applications must be relocatable.  This is a +	 * dummy section as far as we are concerned. +	 */ +	.ascii	".reloc" +	.byte	0 +	.byte	0			// end of 0 padding of section name +	.long	0 +	.long	0 +	.long	0			// SizeOfRawData +	.long	0			// PointerToRawData +	.long	0			// PointerToRelocations +	.long	0			// PointerToLineNumbers +	.short	0			// NumberOfRelocations +	.short	0			// NumberOfLineNumbers +	.long	0x42100040		// Characteristics (section flags) + + +	.ascii	".text" +	.byte	0 +	.byte	0 +	.byte	0        		// end of 0 padding of section name +	.long	_edata - stext		// VirtualSize +	.long	stext - efi_head	// VirtualAddress +	.long	_edata - stext		// SizeOfRawData +	.long	stext - efi_head	// PointerToRawData + +	.long	0		// PointerToRelocations (0 for executables) +	.long	0		// PointerToLineNumbers (0 for executables) +	.short	0		// NumberOfRelocations  (0 for executables) +	.short	0		// NumberOfLineNumbers  (0 for executables) +	.long	0xe0500020	// Characteristics (section flags) +	.align 5 +#endif  ENTRY(stext)  	mov	x21, x0				// x21=FDT +	bl	el2_setup			// Drop to EL1, w20=cpu_boot_mode  	bl	__calc_phys_offset		// x24=PHYS_OFFSET, x28=PHYS_OFFSET-PAGE_OFFSET -	bl	el2_setup			// Drop to EL1 +	bl	set_cpu_boot_mode_flag  	mrs	x22, midr_el1			// x22=cpuid  	mov	x0, x22  	bl	lookup_processor_type @@ -150,21 +264,29 @@ ENDPROC(stext)  /*   * If we're fortunate enough to boot at EL2, ensure that the world is   * sane before dropping to EL1. + * + * Returns either BOOT_CPU_MODE_EL1 or BOOT_CPU_MODE_EL2 in x20 if + * booted in EL1 or EL2 respectively.   */  ENTRY(el2_setup)  	mrs	x0, CurrentEL -	cmp	x0, #PSR_MODE_EL2t -	ccmp	x0, #PSR_MODE_EL2h, #0x4, ne -	ldr	x0, =__boot_cpu_mode		// Compute __boot_cpu_mode -	add	x0, x0, x28 -	b.eq	1f -	str	wzr, [x0]			// Remember we don't have EL2... +	cmp	x0, #CurrentEL_EL2 +	b.ne	1f +	mrs	x0, sctlr_el2 +CPU_BE(	orr	x0, x0, #(1 << 25)	)	// Set the EE bit for EL2 +CPU_LE(	bic	x0, x0, #(1 << 25)	)	// Clear the EE bit for EL2 +	msr	sctlr_el2, x0 +	b	2f +1:	mrs	x0, sctlr_el1 +CPU_BE(	orr	x0, x0, #(3 << 24)	)	// Set the EE and E0E bits for EL1 +CPU_LE(	bic	x0, x0, #(3 << 24)	)	// Clear the EE and E0E bits for EL1 +	msr	sctlr_el1, x0 +	mov	w20, #BOOT_CPU_MODE_EL1		// This cpu booted in EL1 +	isb  	ret  	/* Hyp configuration. */ -1:	ldr	w1, =BOOT_CPU_MODE_EL2 -	str	w1, [x0, #4]			// This CPU has EL2 -	mov	x0, #(1 << 31)			// 64-bit EL1 +2:	mov	x0, #(1 << 31)			// 64-bit EL1  	msr	hcr_el2, x0  	/* Generic timers. */ @@ -181,7 +303,8 @@ ENTRY(el2_setup)  	/* sctlr_el1 */  	mov	x0, #0x0800			// Set/clear RES{1,0} bits -	movk	x0, #0x30d0, lsl #16 +CPU_BE(	movk	x0, #0x33d0, lsl #16	)	// Set EE and E0E on BE systems +CPU_LE(	movk	x0, #0x30d0, lsl #16	)	// Clear EE and E0E on LE systems  	msr	sctlr_el1, x0  	/* Coprocessor traps. */ @@ -204,18 +327,36 @@ ENTRY(el2_setup)  		      PSR_MODE_EL1h)  	msr	spsr_el2, x0  	msr	elr_el2, lr +	mov	w20, #BOOT_CPU_MODE_EL2		// This CPU booted in EL2  	eret  ENDPROC(el2_setup)  /* + * Sets the __boot_cpu_mode flag depending on the CPU boot mode passed + * in x20. See arch/arm64/include/asm/virt.h for more info. + */ +ENTRY(set_cpu_boot_mode_flag) +	ldr	x1, =__boot_cpu_mode		// Compute __boot_cpu_mode +	add	x1, x1, x28 +	cmp	w20, #BOOT_CPU_MODE_EL2 +	b.ne	1f +	add	x1, x1, #4 +1:	str	w20, [x1]			// This CPU has booted in EL1 +	dmb	sy +	dc	ivac, x1			// Invalidate potentially stale cache line +	ret +ENDPROC(set_cpu_boot_mode_flag) + +/*   * We need to find out the CPU boot mode long after boot, so we need to   * store it in a writable variable.   *   * This is not in .bss, because we set it sufficiently early that the boot-time   * zeroing of .bss would clobber it.   */ -	.pushsection	.data +	.pushsection	.data..cacheline_aligned  ENTRY(__boot_cpu_mode) +	.align	L1_CACHE_SHIFT  	.long	BOOT_CPU_MODE_EL2  	.long	0  	.popsection @@ -225,7 +366,6 @@ ENTRY(__boot_cpu_mode)  	.quad	PAGE_OFFSET  #ifdef CONFIG_SMP -	.pushsection    .smp.pen.text, "ax"  	.align	3  1:	.quad	.  	.quad	secondary_holding_pen_release @@ -235,8 +375,9 @@ ENTRY(__boot_cpu_mode)  	 * cores are held until we're ready for them to initialise.  	 */  ENTRY(secondary_holding_pen) -	bl	__calc_phys_offset		// x24=phys offset -	bl	el2_setup			// Drop to EL1 +	bl	el2_setup			// Drop to EL1, w20=cpu_boot_mode +	bl	__calc_phys_offset		// x24=PHYS_OFFSET, x28=PHYS_OFFSET-PAGE_OFFSET +	bl	set_cpu_boot_mode_flag  	mrs	x0, mpidr_el1  	ldr     x1, =MPIDR_HWID_BITMASK  	and	x0, x0, x1 @@ -250,7 +391,17 @@ pen:	ldr	x4, [x3]  	wfe  	b	pen  ENDPROC(secondary_holding_pen) -	.popsection + +	/* +	 * Secondary entry point that jumps straight into the kernel. Only to +	 * be used where CPUs are brought online dynamically by the kernel. +	 */ +ENTRY(secondary_entry) +	bl	el2_setup			// Drop to EL1 +	bl	__calc_phys_offset		// x24=PHYS_OFFSET, x28=PHYS_OFFSET-PAGE_OFFSET +	bl	set_cpu_boot_mode_flag +	b	secondary_startup +ENDPROC(secondary_entry)  ENTRY(secondary_startup)  	/* @@ -348,26 +499,18 @@ ENDPROC(__calc_phys_offset)   * Preserves:	tbl, flags   * Corrupts:	phys, start, end, pstate   */ -	.macro	create_block_map, tbl, flags, phys, start, end, idmap=0 +	.macro	create_block_map, tbl, flags, phys, start, end  	lsr	\phys, \phys, #BLOCK_SHIFT -	.if	\idmap -	and	\start, \phys, #PTRS_PER_PTE - 1	// table index -	.else  	lsr	\start, \start, #BLOCK_SHIFT  	and	\start, \start, #PTRS_PER_PTE - 1	// table index -	.endif  	orr	\phys, \flags, \phys, lsl #BLOCK_SHIFT	// table entry -	.ifnc	\start,\end  	lsr	\end, \end, #BLOCK_SHIFT  	and	\end, \end, #PTRS_PER_PTE - 1		// table end index -	.endif  9999:	str	\phys, [\tbl, \start, lsl #3]		// store the entry -	.ifnc	\start,\end  	add	\start, \start, #1			// next entry  	add	\phys, \phys, #BLOCK_SIZE		// next block  	cmp	\start, \end  	b.ls	9999b -	.endif  	.endm  /* @@ -376,10 +519,19 @@ ENDPROC(__calc_phys_offset)   *   - identity mapping to enable the MMU (low address, TTBR0)   *   - first few MB of the kernel linear mapping to jump to once the MMU has   *     been enabled, including the FDT blob (TTBR1) - *   - UART mapping if CONFIG_EARLY_PRINTK is enabled (TTBR1) + *   - pgd entry for fixed mappings (TTBR1)   */  __create_page_tables:  	pgtbl	x25, x26, x24			// idmap_pg_dir and swapper_pg_dir addresses +	mov	x27, lr + +	/* +	 * Invalidate the idmap and swapper page tables to avoid potential +	 * dirty cache lines being evicted. +	 */ +	mov	x0, x25 +	add	x1, x26, #SWAPPER_DIR_SIZE +	bl	__inval_cache_range  	/*  	 * Clear the idmap and swapper page tables. @@ -399,9 +551,13 @@ __create_page_tables:  	 * Create the identity mapping.  	 */  	add	x0, x25, #PAGE_SIZE		// section table address -	adr	x3, __turn_mmu_on		// virtual/physical address +	ldr	x3, =KERNEL_START +	add	x3, x3, x28			// __pa(KERNEL_START)  	create_pgd_entry x25, x0, x3, x5, x6 -	create_block_map x0, x7, x3, x5, x5, idmap=1 +	ldr	x6, =KERNEL_END +	mov	x5, x3				// __pa(KERNEL_START) +	add	x6, x6, x28			// __pa(KERNEL_END) +	create_block_map x0, x7, x3, x5, x6  	/*  	 * Map the kernel image (starting with PHYS_OFFSET). @@ -409,7 +565,7 @@ __create_page_tables:  	add	x0, x26, #PAGE_SIZE		// section table address  	mov	x5, #PAGE_OFFSET  	create_pgd_entry x26, x0, x5, x3, x6 -	ldr	x6, =KERNEL_END - 1 +	ldr	x6, =KERNEL_END  	mov	x3, x24				// phys offset  	create_block_map x0, x7, x3, x5, x6 @@ -429,15 +585,23 @@ __create_page_tables:  	sub	x6, x6, #1			// inclusive range  	create_block_map x0, x7, x3, x5, x6  1: -#ifdef CONFIG_EARLY_PRINTK  	/* -	 * Create the pgd entry for the UART mapping. The full mapping is done -	 * later based earlyprintk kernel parameter. +	 * Create the pgd entry for the fixed mappings.  	 */ -	ldr	x5, =EARLYCON_IOBASE		// UART virtual address +	ldr	x5, =FIXADDR_TOP		// Fixed mapping virtual address  	add	x0, x26, #2 * PAGE_SIZE		// section table address  	create_pgd_entry x26, x0, x5, x6, x7 -#endif + +	/* +	 * Since the page tables have been populated with non-cacheable +	 * accesses (MMU disabled), invalidate the idmap and swapper page +	 * tables again to remove any speculatively loaded cache lines. +	 */ +	mov	x0, x25 +	add	x1, x26, #SWAPPER_DIR_SIZE +	bl	__inval_cache_range + +	mov	lr, x27  	ret  ENDPROC(__create_page_tables)  	.ltorg @@ -446,8 +610,6 @@ ENDPROC(__create_page_tables)  	.type	__switch_data, %object  __switch_data:  	.quad	__mmap_switched -	.quad	__data_loc			// x4 -	.quad	_data				// x5  	.quad	__bss_start			// x6  	.quad	_end				// x7  	.quad	processor_id			// x4 @@ -462,15 +624,7 @@ __switch_data:  __mmap_switched:  	adr	x3, __switch_data + 8 -	ldp	x4, x5, [x3], #16  	ldp	x6, x7, [x3], #16 -	cmp	x4, x5				// Copy data segment if needed -1:	ccmp	x5, x6, #4, ne -	b.eq	2f -	ldr	x16, [x4], #8 -	str	x16, [x5], #8 -	b	1b -2:  1:	cmp	x6, x7  	b.hs	2f  	str	xzr, [x6], #8			// Clear BSS diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index 329218ca9ff..df1cf15377b 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c @@ -20,13 +20,14 @@  #define pr_fmt(fmt) "hw-breakpoint: " fmt +#include <linux/compat.h> +#include <linux/cpu_pm.h>  #include <linux/errno.h>  #include <linux/hw_breakpoint.h>  #include <linux/perf_event.h>  #include <linux/ptrace.h>  #include <linux/smp.h> -#include <asm/compat.h>  #include <asm/current.h>  #include <asm/debug-monitors.h>  #include <asm/hw_breakpoint.h> @@ -169,94 +170,134 @@ static enum debug_el debug_exception_level(int privilege)  	}  } -/* - * Install a perf counter breakpoint. +enum hw_breakpoint_ops { +	HW_BREAKPOINT_INSTALL, +	HW_BREAKPOINT_UNINSTALL, +	HW_BREAKPOINT_RESTORE +}; + +/** + * hw_breakpoint_slot_setup - Find and setup a perf slot according to + *			      operations + * + * @slots: pointer to array of slots + * @max_slots: max number of slots + * @bp: perf_event to setup + * @ops: operation to be carried out on the slot + * + * Return: + *	slot index on success + *	-ENOSPC if no slot is available/matches + *	-EINVAL on wrong operations parameter   */ -int arch_install_hw_breakpoint(struct perf_event *bp) +static int hw_breakpoint_slot_setup(struct perf_event **slots, int max_slots, +				    struct perf_event *bp, +				    enum hw_breakpoint_ops ops) +{ +	int i; +	struct perf_event **slot; + +	for (i = 0; i < max_slots; ++i) { +		slot = &slots[i]; +		switch (ops) { +		case HW_BREAKPOINT_INSTALL: +			if (!*slot) { +				*slot = bp; +				return i; +			} +			break; +		case HW_BREAKPOINT_UNINSTALL: +			if (*slot == bp) { +				*slot = NULL; +				return i; +			} +			break; +		case HW_BREAKPOINT_RESTORE: +			if (*slot == bp) +				return i; +			break; +		default: +			pr_warn_once("Unhandled hw breakpoint ops %d\n", ops); +			return -EINVAL; +		} +	} +	return -ENOSPC; +} + +static int hw_breakpoint_control(struct perf_event *bp, +				 enum hw_breakpoint_ops ops)  {  	struct arch_hw_breakpoint *info = counter_arch_bp(bp); -	struct perf_event **slot, **slots; +	struct perf_event **slots;  	struct debug_info *debug_info = ¤t->thread.debug;  	int i, max_slots, ctrl_reg, val_reg, reg_enable; +	enum debug_el dbg_el = debug_exception_level(info->ctrl.privilege);  	u32 ctrl;  	if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) {  		/* Breakpoint */  		ctrl_reg = AARCH64_DBG_REG_BCR;  		val_reg = AARCH64_DBG_REG_BVR; -		slots = __get_cpu_var(bp_on_reg); +		slots = this_cpu_ptr(bp_on_reg);  		max_slots = core_num_brps;  		reg_enable = !debug_info->bps_disabled;  	} else {  		/* Watchpoint */  		ctrl_reg = AARCH64_DBG_REG_WCR;  		val_reg = AARCH64_DBG_REG_WVR; -		slots = __get_cpu_var(wp_on_reg); +		slots = this_cpu_ptr(wp_on_reg);  		max_slots = core_num_wrps;  		reg_enable = !debug_info->wps_disabled;  	} -	for (i = 0; i < max_slots; ++i) { -		slot = &slots[i]; - -		if (!*slot) { -			*slot = bp; -			break; -		} -	} - -	if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot")) -		return -ENOSPC; +	i = hw_breakpoint_slot_setup(slots, max_slots, bp, ops); -	/* Ensure debug monitors are enabled at the correct exception level.  */ -	enable_debug_monitors(debug_exception_level(info->ctrl.privilege)); +	if (WARN_ONCE(i < 0, "Can't find any breakpoint slot")) +		return i; -	/* Setup the address register. */ -	write_wb_reg(val_reg, i, info->address); +	switch (ops) { +	case HW_BREAKPOINT_INSTALL: +		/* +		 * Ensure debug monitors are enabled at the correct exception +		 * level. +		 */ +		enable_debug_monitors(dbg_el); +		/* Fall through */ +	case HW_BREAKPOINT_RESTORE: +		/* Setup the address register. */ +		write_wb_reg(val_reg, i, info->address); + +		/* Setup the control register. */ +		ctrl = encode_ctrl_reg(info->ctrl); +		write_wb_reg(ctrl_reg, i, +			     reg_enable ? ctrl | 0x1 : ctrl & ~0x1); +		break; +	case HW_BREAKPOINT_UNINSTALL: +		/* Reset the control register. */ +		write_wb_reg(ctrl_reg, i, 0); -	/* Setup the control register. */ -	ctrl = encode_ctrl_reg(info->ctrl); -	write_wb_reg(ctrl_reg, i, reg_enable ? ctrl | 0x1 : ctrl & ~0x1); +		/* +		 * Release the debug monitors for the correct exception +		 * level. +		 */ +		disable_debug_monitors(dbg_el); +		break; +	}  	return 0;  } -void arch_uninstall_hw_breakpoint(struct perf_event *bp) +/* + * Install a perf counter breakpoint. + */ +int arch_install_hw_breakpoint(struct perf_event *bp)  { -	struct arch_hw_breakpoint *info = counter_arch_bp(bp); -	struct perf_event **slot, **slots; -	int i, max_slots, base; - -	if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) { -		/* Breakpoint */ -		base = AARCH64_DBG_REG_BCR; -		slots = __get_cpu_var(bp_on_reg); -		max_slots = core_num_brps; -	} else { -		/* Watchpoint */ -		base = AARCH64_DBG_REG_WCR; -		slots = __get_cpu_var(wp_on_reg); -		max_slots = core_num_wrps; -	} - -	/* Remove the breakpoint. */ -	for (i = 0; i < max_slots; ++i) { -		slot = &slots[i]; - -		if (*slot == bp) { -			*slot = NULL; -			break; -		} -	} - -	if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot")) -		return; - -	/* Reset the control register. */ -	write_wb_reg(base, i, 0); +	return hw_breakpoint_control(bp, HW_BREAKPOINT_INSTALL); +} -	/* Release the debug monitors for the correct exception level.  */ -	disable_debug_monitors(debug_exception_level(info->ctrl.privilege)); +void arch_uninstall_hw_breakpoint(struct perf_event *bp) +{ +	hw_breakpoint_control(bp, HW_BREAKPOINT_UNINSTALL);  }  static int get_hbp_len(u8 hbp_len) @@ -505,11 +546,11 @@ static void toggle_bp_registers(int reg, enum debug_el el, int enable)  	switch (reg) {  	case AARCH64_DBG_REG_BCR: -		slots = __get_cpu_var(bp_on_reg); +		slots = this_cpu_ptr(bp_on_reg);  		max_slots = core_num_brps;  		break;  	case AARCH64_DBG_REG_WCR: -		slots = __get_cpu_var(wp_on_reg); +		slots = this_cpu_ptr(wp_on_reg);  		max_slots = core_num_wrps;  		break;  	default: @@ -546,7 +587,7 @@ static int breakpoint_handler(unsigned long unused, unsigned int esr,  	struct debug_info *debug_info;  	struct arch_hw_breakpoint_ctrl ctrl; -	slots = (struct perf_event **)__get_cpu_var(bp_on_reg); +	slots = this_cpu_ptr(bp_on_reg);  	addr = instruction_pointer(regs);  	debug_info = ¤t->thread.debug; @@ -596,7 +637,7 @@ unlock:  			user_enable_single_step(current);  	} else {  		toggle_bp_registers(AARCH64_DBG_REG_BCR, DBG_ACTIVE_EL1, 0); -		kernel_step = &__get_cpu_var(stepping_kernel_bp); +		kernel_step = this_cpu_ptr(&stepping_kernel_bp);  		if (*kernel_step != ARM_KERNEL_STEP_NONE)  			return 0; @@ -623,7 +664,7 @@ static int watchpoint_handler(unsigned long addr, unsigned int esr,  	struct arch_hw_breakpoint *info;  	struct arch_hw_breakpoint_ctrl ctrl; -	slots = (struct perf_event **)__get_cpu_var(wp_on_reg); +	slots = this_cpu_ptr(wp_on_reg);  	debug_info = ¤t->thread.debug;  	for (i = 0; i < core_num_wrps; ++i) { @@ -698,7 +739,7 @@ unlock:  			user_enable_single_step(current);  	} else {  		toggle_bp_registers(AARCH64_DBG_REG_WCR, DBG_ACTIVE_EL1, 0); -		kernel_step = &__get_cpu_var(stepping_kernel_bp); +		kernel_step = this_cpu_ptr(&stepping_kernel_bp);  		if (*kernel_step != ARM_KERNEL_STEP_NONE)  			return 0; @@ -722,7 +763,7 @@ int reinstall_suspended_bps(struct pt_regs *regs)  	struct debug_info *debug_info = ¤t->thread.debug;  	int handled_exception = 0, *kernel_step; -	kernel_step = &__get_cpu_var(stepping_kernel_bp); +	kernel_step = this_cpu_ptr(&stepping_kernel_bp);  	/*  	 * Called from single-step exception handler. @@ -806,18 +847,36 @@ void hw_breakpoint_thread_switch(struct task_struct *next)  /*   * CPU initialisation.   */ -static void reset_ctrl_regs(void *unused) +static void hw_breakpoint_reset(void *unused)  {  	int i; - -	for (i = 0; i < core_num_brps; ++i) { -		write_wb_reg(AARCH64_DBG_REG_BCR, i, 0UL); -		write_wb_reg(AARCH64_DBG_REG_BVR, i, 0UL); +	struct perf_event **slots; +	/* +	 * When a CPU goes through cold-boot, it does not have any installed +	 * slot, so it is safe to share the same function for restoring and +	 * resetting breakpoints; when a CPU is hotplugged in, it goes +	 * through the slots, which are all empty, hence it just resets control +	 * and value for debug registers. +	 * When this function is triggered on warm-boot through a CPU PM +	 * notifier some slots might be initialized; if so they are +	 * reprogrammed according to the debug slots content. +	 */ +	for (slots = this_cpu_ptr(bp_on_reg), i = 0; i < core_num_brps; ++i) { +		if (slots[i]) { +			hw_breakpoint_control(slots[i], HW_BREAKPOINT_RESTORE); +		} else { +			write_wb_reg(AARCH64_DBG_REG_BCR, i, 0UL); +			write_wb_reg(AARCH64_DBG_REG_BVR, i, 0UL); +		}  	} -	for (i = 0; i < core_num_wrps; ++i) { -		write_wb_reg(AARCH64_DBG_REG_WCR, i, 0UL); -		write_wb_reg(AARCH64_DBG_REG_WVR, i, 0UL); +	for (slots = this_cpu_ptr(wp_on_reg), i = 0; i < core_num_wrps; ++i) { +		if (slots[i]) { +			hw_breakpoint_control(slots[i], HW_BREAKPOINT_RESTORE); +		} else { +			write_wb_reg(AARCH64_DBG_REG_WCR, i, 0UL); +			write_wb_reg(AARCH64_DBG_REG_WVR, i, 0UL); +		}  	}  } @@ -827,7 +886,7 @@ static int hw_breakpoint_reset_notify(struct notifier_block *self,  {  	int cpu = (long)hcpu;  	if (action == CPU_ONLINE) -		smp_call_function_single(cpu, reset_ctrl_regs, NULL, 1); +		smp_call_function_single(cpu, hw_breakpoint_reset, NULL, 1);  	return NOTIFY_OK;  } @@ -835,6 +894,14 @@ static struct notifier_block hw_breakpoint_reset_nb = {  	.notifier_call = hw_breakpoint_reset_notify,  }; +#ifdef CONFIG_ARM64_CPU_SUSPEND +extern void cpu_suspend_set_dbg_restorer(void (*hw_bp_restore)(void *)); +#else +static inline void cpu_suspend_set_dbg_restorer(void (*hw_bp_restore)(void *)) +{ +} +#endif +  /*   * One-time initialisation.   */ @@ -846,12 +913,14 @@ static int __init arch_hw_breakpoint_init(void)  	pr_info("found %d breakpoint and %d watchpoint registers.\n",  		core_num_brps, core_num_wrps); +	cpu_notifier_register_begin(); +  	/*  	 * Reset the breakpoint resources. We assume that a halting  	 * debugger will leave the world in a nice state for us.  	 */ -	smp_call_function(reset_ctrl_regs, NULL, 1); -	reset_ctrl_regs(NULL); +	smp_call_function(hw_breakpoint_reset, NULL, 1); +	hw_breakpoint_reset(NULL);  	/* Register debug fault handlers. */  	hook_debug_fault_code(DBG_ESR_EVT_HWBP, breakpoint_handler, SIGTRAP, @@ -860,7 +929,12 @@ static int __init arch_hw_breakpoint_init(void)  			      TRAP_HWBKPT, "hw-watchpoint handler");  	/* Register hotplug notifier. */ -	register_cpu_notifier(&hw_breakpoint_reset_nb); +	__register_cpu_notifier(&hw_breakpoint_reset_nb); + +	cpu_notifier_register_done(); + +	/* Register cpu_suspend hw breakpoint restore hook */ +	cpu_suspend_set_dbg_restorer(hw_breakpoint_reset);  	return 0;  } diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c new file mode 100644 index 00000000000..92f36835486 --- /dev/null +++ b/arch/arm64/kernel/insn.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2013 Huawei Ltd. + * Author: Jiang Liu <liuj97@gmail.com> + * + * 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 <linux/bitops.h> +#include <linux/compiler.h> +#include <linux/kernel.h> +#include <linux/smp.h> +#include <linux/stop_machine.h> +#include <linux/uaccess.h> +#include <asm/cacheflush.h> +#include <asm/insn.h> + +static int aarch64_insn_encoding_class[] = { +	AARCH64_INSN_CLS_UNKNOWN, +	AARCH64_INSN_CLS_UNKNOWN, +	AARCH64_INSN_CLS_UNKNOWN, +	AARCH64_INSN_CLS_UNKNOWN, +	AARCH64_INSN_CLS_LDST, +	AARCH64_INSN_CLS_DP_REG, +	AARCH64_INSN_CLS_LDST, +	AARCH64_INSN_CLS_DP_FPSIMD, +	AARCH64_INSN_CLS_DP_IMM, +	AARCH64_INSN_CLS_DP_IMM, +	AARCH64_INSN_CLS_BR_SYS, +	AARCH64_INSN_CLS_BR_SYS, +	AARCH64_INSN_CLS_LDST, +	AARCH64_INSN_CLS_DP_REG, +	AARCH64_INSN_CLS_LDST, +	AARCH64_INSN_CLS_DP_FPSIMD, +}; + +enum aarch64_insn_encoding_class __kprobes aarch64_get_insn_class(u32 insn) +{ +	return aarch64_insn_encoding_class[(insn >> 25) & 0xf]; +} + +/* NOP is an alias of HINT */ +bool __kprobes aarch64_insn_is_nop(u32 insn) +{ +	if (!aarch64_insn_is_hint(insn)) +		return false; + +	switch (insn & 0xFE0) { +	case AARCH64_INSN_HINT_YIELD: +	case AARCH64_INSN_HINT_WFE: +	case AARCH64_INSN_HINT_WFI: +	case AARCH64_INSN_HINT_SEV: +	case AARCH64_INSN_HINT_SEVL: +		return false; +	default: +		return true; +	} +} + +/* + * In ARMv8-A, A64 instructions have a fixed length of 32 bits and are always + * little-endian. + */ +int __kprobes aarch64_insn_read(void *addr, u32 *insnp) +{ +	int ret; +	u32 val; + +	ret = probe_kernel_read(&val, addr, AARCH64_INSN_SIZE); +	if (!ret) +		*insnp = le32_to_cpu(val); + +	return ret; +} + +int __kprobes aarch64_insn_write(void *addr, u32 insn) +{ +	insn = cpu_to_le32(insn); +	return probe_kernel_write(addr, &insn, AARCH64_INSN_SIZE); +} + +static bool __kprobes __aarch64_insn_hotpatch_safe(u32 insn) +{ +	if (aarch64_get_insn_class(insn) != AARCH64_INSN_CLS_BR_SYS) +		return false; + +	return	aarch64_insn_is_b(insn) || +		aarch64_insn_is_bl(insn) || +		aarch64_insn_is_svc(insn) || +		aarch64_insn_is_hvc(insn) || +		aarch64_insn_is_smc(insn) || +		aarch64_insn_is_brk(insn) || +		aarch64_insn_is_nop(insn); +} + +/* + * ARM Architecture Reference Manual for ARMv8 Profile-A, Issue A.a + * Section B2.6.5 "Concurrent modification and execution of instructions": + * Concurrent modification and execution of instructions can lead to the + * resulting instruction performing any behavior that can be achieved by + * executing any sequence of instructions that can be executed from the + * same Exception level, except where the instruction before modification + * and the instruction after modification is a B, BL, NOP, BKPT, SVC, HVC, + * or SMC instruction. + */ +bool __kprobes aarch64_insn_hotpatch_safe(u32 old_insn, u32 new_insn) +{ +	return __aarch64_insn_hotpatch_safe(old_insn) && +	       __aarch64_insn_hotpatch_safe(new_insn); +} + +int __kprobes aarch64_insn_patch_text_nosync(void *addr, u32 insn) +{ +	u32 *tp = addr; +	int ret; + +	/* A64 instructions must be word aligned */ +	if ((uintptr_t)tp & 0x3) +		return -EINVAL; + +	ret = aarch64_insn_write(tp, insn); +	if (ret == 0) +		flush_icache_range((uintptr_t)tp, +				   (uintptr_t)tp + AARCH64_INSN_SIZE); + +	return ret; +} + +struct aarch64_insn_patch { +	void		**text_addrs; +	u32		*new_insns; +	int		insn_cnt; +	atomic_t	cpu_count; +}; + +static int __kprobes aarch64_insn_patch_text_cb(void *arg) +{ +	int i, ret = 0; +	struct aarch64_insn_patch *pp = arg; + +	/* The first CPU becomes master */ +	if (atomic_inc_return(&pp->cpu_count) == 1) { +		for (i = 0; ret == 0 && i < pp->insn_cnt; i++) +			ret = aarch64_insn_patch_text_nosync(pp->text_addrs[i], +							     pp->new_insns[i]); +		/* +		 * aarch64_insn_patch_text_nosync() calls flush_icache_range(), +		 * which ends with "dsb; isb" pair guaranteeing global +		 * visibility. +		 */ +		atomic_set(&pp->cpu_count, -1); +	} else { +		while (atomic_read(&pp->cpu_count) != -1) +			cpu_relax(); +		isb(); +	} + +	return ret; +} + +int __kprobes aarch64_insn_patch_text_sync(void *addrs[], u32 insns[], int cnt) +{ +	struct aarch64_insn_patch patch = { +		.text_addrs = addrs, +		.new_insns = insns, +		.insn_cnt = cnt, +		.cpu_count = ATOMIC_INIT(0), +	}; + +	if (cnt <= 0) +		return -EINVAL; + +	return stop_machine(aarch64_insn_patch_text_cb, &patch, +			    cpu_online_mask); +} + +int __kprobes aarch64_insn_patch_text(void *addrs[], u32 insns[], int cnt) +{ +	int ret; +	u32 insn; + +	/* Unsafe to patch multiple instructions without synchronizaiton */ +	if (cnt == 1) { +		ret = aarch64_insn_read(addrs[0], &insn); +		if (ret) +			return ret; + +		if (aarch64_insn_hotpatch_safe(insn, insns[0])) { +			/* +			 * ARMv8 architecture doesn't guarantee all CPUs see +			 * the new instruction after returning from function +			 * aarch64_insn_patch_text_nosync(). So send IPIs to +			 * all other CPUs to achieve instruction +			 * synchronization. +			 */ +			ret = aarch64_insn_patch_text_nosync(addrs[0], insns[0]); +			kick_all_cpus_sync(); +			return ret; +		} +	} + +	return aarch64_insn_patch_text_sync(addrs, insns, cnt); +} + +u32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type, +				  u32 insn, u64 imm) +{ +	u32 immlo, immhi, lomask, himask, mask; +	int shift; + +	switch (type) { +	case AARCH64_INSN_IMM_ADR: +		lomask = 0x3; +		himask = 0x7ffff; +		immlo = imm & lomask; +		imm >>= 2; +		immhi = imm & himask; +		imm = (immlo << 24) | (immhi); +		mask = (lomask << 24) | (himask); +		shift = 5; +		break; +	case AARCH64_INSN_IMM_26: +		mask = BIT(26) - 1; +		shift = 0; +		break; +	case AARCH64_INSN_IMM_19: +		mask = BIT(19) - 1; +		shift = 5; +		break; +	case AARCH64_INSN_IMM_16: +		mask = BIT(16) - 1; +		shift = 5; +		break; +	case AARCH64_INSN_IMM_14: +		mask = BIT(14) - 1; +		shift = 5; +		break; +	case AARCH64_INSN_IMM_12: +		mask = BIT(12) - 1; +		shift = 10; +		break; +	case AARCH64_INSN_IMM_9: +		mask = BIT(9) - 1; +		shift = 12; +		break; +	default: +		pr_err("aarch64_insn_encode_immediate: unknown immediate encoding %d\n", +			type); +		return 0; +	} + +	/* Update the immediate field. */ +	insn &= ~(mask << shift); +	insn |= (imm & mask) << shift; + +	return insn; +} + +u32 __kprobes aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr, +					  enum aarch64_insn_branch_type type) +{ +	u32 insn; +	long offset; + +	/* +	 * PC: A 64-bit Program Counter holding the address of the current +	 * instruction. A64 instructions must be word-aligned. +	 */ +	BUG_ON((pc & 0x3) || (addr & 0x3)); + +	/* +	 * B/BL support [-128M, 128M) offset +	 * ARM64 virtual address arrangement guarantees all kernel and module +	 * texts are within +/-128M. +	 */ +	offset = ((long)addr - (long)pc); +	BUG_ON(offset < -SZ_128M || offset >= SZ_128M); + +	if (type == AARCH64_INSN_BRANCH_LINK) +		insn = aarch64_insn_get_bl_value(); +	else +		insn = aarch64_insn_get_b_value(); + +	return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_26, insn, +					     offset >> 2); +} + +u32 __kprobes aarch64_insn_gen_hint(enum aarch64_insn_hint_op op) +{ +	return aarch64_insn_get_hint_value() | op; +} + +u32 __kprobes aarch64_insn_gen_nop(void) +{ +	return aarch64_insn_gen_hint(AARCH64_INSN_HINT_NOP); +} diff --git a/arch/arm64/kernel/irq.c b/arch/arm64/kernel/irq.c index ecb3354292e..0f08dfd69eb 100644 --- a/arch/arm64/kernel/irq.c +++ b/arch/arm64/kernel/irq.c @@ -81,3 +81,68 @@ void __init init_IRQ(void)  	if (!handle_arch_irq)  		panic("No interrupt controller found.");  } + +#ifdef CONFIG_HOTPLUG_CPU +static bool migrate_one_irq(struct irq_desc *desc) +{ +	struct irq_data *d = irq_desc_get_irq_data(desc); +	const struct cpumask *affinity = d->affinity; +	struct irq_chip *c; +	bool ret = false; + +	/* +	 * If this is a per-CPU interrupt, or the affinity does not +	 * include this CPU, then we have nothing to do. +	 */ +	if (irqd_is_per_cpu(d) || !cpumask_test_cpu(smp_processor_id(), affinity)) +		return false; + +	if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) +		ret = true; + +	/* +	 * when using forced irq_set_affinity we must ensure that the cpu +	 * being offlined is not present in the affinity mask, it may be +	 * selected as the target CPU otherwise +	 */ +	affinity = cpu_online_mask; +	c = irq_data_get_irq_chip(d); +	if (!c->irq_set_affinity) +		pr_debug("IRQ%u: unable to set affinity\n", d->irq); +	else if (c->irq_set_affinity(d, affinity, true) == IRQ_SET_MASK_OK && ret) +		cpumask_copy(d->affinity, affinity); + +	return ret; +} + +/* + * The current CPU has been marked offline.  Migrate IRQs off this CPU. + * If the affinity settings do not allow other CPUs, force them onto any + * available CPU. + * + * Note: we must iterate over all IRQs, whether they have an attached + * action structure or not, as we need to get chained interrupts too. + */ +void migrate_irqs(void) +{ +	unsigned int i; +	struct irq_desc *desc; +	unsigned long flags; + +	local_irq_save(flags); + +	for_each_irq_desc(i, desc) { +		bool affinity_broken; + +		raw_spin_lock(&desc->lock); +		affinity_broken = migrate_one_irq(desc); +		raw_spin_unlock(&desc->lock); + +		if (affinity_broken) +			pr_warn_ratelimited("IRQ%u no longer affine to CPU%u\n", +					    i, smp_processor_id()); +	} + +	local_irq_restore(flags); +} +#endif /* CONFIG_HOTPLUG_CPU */ diff --git a/arch/arm64/kernel/jump_label.c b/arch/arm64/kernel/jump_label.c new file mode 100644 index 00000000000..263a166291f --- /dev/null +++ b/arch/arm64/kernel/jump_label.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013 Huawei Ltd. + * Author: Jiang Liu <liuj97@gmail.com> + * + * Based on arch/arm/kernel/jump_label.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. + * + * 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 <linux/kernel.h> +#include <linux/jump_label.h> +#include <asm/insn.h> + +#ifdef HAVE_JUMP_LABEL + +static void __arch_jump_label_transform(struct jump_entry *entry, +					enum jump_label_type type, +					bool is_static) +{ +	void *addr = (void *)entry->code; +	u32 insn; + +	if (type == JUMP_LABEL_ENABLE) { +		insn = aarch64_insn_gen_branch_imm(entry->code, +						   entry->target, +						   AARCH64_INSN_BRANCH_NOLINK); +	} else { +		insn = aarch64_insn_gen_nop(); +	} + +	if (is_static) +		aarch64_insn_patch_text_nosync(addr, insn); +	else +		aarch64_insn_patch_text(&addr, &insn, 1); +} + +void arch_jump_label_transform(struct jump_entry *entry, +			       enum jump_label_type type) +{ +	__arch_jump_label_transform(entry, type, false); +} + +void arch_jump_label_transform_static(struct jump_entry *entry, +				      enum jump_label_type type) +{ +	__arch_jump_label_transform(entry, type, true); +} + +#endif	/* HAVE_JUMP_LABEL */ diff --git a/arch/arm64/kernel/kgdb.c b/arch/arm64/kernel/kgdb.c new file mode 100644 index 00000000000..75c9cf1aafe --- /dev/null +++ b/arch/arm64/kernel/kgdb.c @@ -0,0 +1,336 @@ +/* + * AArch64 KGDB support + * + * Based on arch/arm/kernel/kgdb.c + * + * Copyright (C) 2013 Cavium Inc. + * Author: Vijaya Kumar K <vijaya.kumar@caviumnetworks.com> + * + * 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 <linux/irq.h> +#include <linux/kdebug.h> +#include <linux/kgdb.h> +#include <asm/traps.h> + +struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = { +	{ "x0", 8, offsetof(struct pt_regs, regs[0])}, +	{ "x1", 8, offsetof(struct pt_regs, regs[1])}, +	{ "x2", 8, offsetof(struct pt_regs, regs[2])}, +	{ "x3", 8, offsetof(struct pt_regs, regs[3])}, +	{ "x4", 8, offsetof(struct pt_regs, regs[4])}, +	{ "x5", 8, offsetof(struct pt_regs, regs[5])}, +	{ "x6", 8, offsetof(struct pt_regs, regs[6])}, +	{ "x7", 8, offsetof(struct pt_regs, regs[7])}, +	{ "x8", 8, offsetof(struct pt_regs, regs[8])}, +	{ "x9", 8, offsetof(struct pt_regs, regs[9])}, +	{ "x10", 8, offsetof(struct pt_regs, regs[10])}, +	{ "x11", 8, offsetof(struct pt_regs, regs[11])}, +	{ "x12", 8, offsetof(struct pt_regs, regs[12])}, +	{ "x13", 8, offsetof(struct pt_regs, regs[13])}, +	{ "x14", 8, offsetof(struct pt_regs, regs[14])}, +	{ "x15", 8, offsetof(struct pt_regs, regs[15])}, +	{ "x16", 8, offsetof(struct pt_regs, regs[16])}, +	{ "x17", 8, offsetof(struct pt_regs, regs[17])}, +	{ "x18", 8, offsetof(struct pt_regs, regs[18])}, +	{ "x19", 8, offsetof(struct pt_regs, regs[19])}, +	{ "x20", 8, offsetof(struct pt_regs, regs[20])}, +	{ "x21", 8, offsetof(struct pt_regs, regs[21])}, +	{ "x22", 8, offsetof(struct pt_regs, regs[22])}, +	{ "x23", 8, offsetof(struct pt_regs, regs[23])}, +	{ "x24", 8, offsetof(struct pt_regs, regs[24])}, +	{ "x25", 8, offsetof(struct pt_regs, regs[25])}, +	{ "x26", 8, offsetof(struct pt_regs, regs[26])}, +	{ "x27", 8, offsetof(struct pt_regs, regs[27])}, +	{ "x28", 8, offsetof(struct pt_regs, regs[28])}, +	{ "x29", 8, offsetof(struct pt_regs, regs[29])}, +	{ "x30", 8, offsetof(struct pt_regs, regs[30])}, +	{ "sp", 8, offsetof(struct pt_regs, sp)}, +	{ "pc", 8, offsetof(struct pt_regs, pc)}, +	{ "pstate", 8, offsetof(struct pt_regs, pstate)}, +	{ "v0", 16, -1 }, +	{ "v1", 16, -1 }, +	{ "v2", 16, -1 }, +	{ "v3", 16, -1 }, +	{ "v4", 16, -1 }, +	{ "v5", 16, -1 }, +	{ "v6", 16, -1 }, +	{ "v7", 16, -1 }, +	{ "v8", 16, -1 }, +	{ "v9", 16, -1 }, +	{ "v10", 16, -1 }, +	{ "v11", 16, -1 }, +	{ "v12", 16, -1 }, +	{ "v13", 16, -1 }, +	{ "v14", 16, -1 }, +	{ "v15", 16, -1 }, +	{ "v16", 16, -1 }, +	{ "v17", 16, -1 }, +	{ "v18", 16, -1 }, +	{ "v19", 16, -1 }, +	{ "v20", 16, -1 }, +	{ "v21", 16, -1 }, +	{ "v22", 16, -1 }, +	{ "v23", 16, -1 }, +	{ "v24", 16, -1 }, +	{ "v25", 16, -1 }, +	{ "v26", 16, -1 }, +	{ "v27", 16, -1 }, +	{ "v28", 16, -1 }, +	{ "v29", 16, -1 }, +	{ "v30", 16, -1 }, +	{ "v31", 16, -1 }, +	{ "fpsr", 4, -1 }, +	{ "fpcr", 4, -1 }, +}; + +char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs) +{ +	if (regno >= DBG_MAX_REG_NUM || regno < 0) +		return NULL; + +	if (dbg_reg_def[regno].offset != -1) +		memcpy(mem, (void *)regs + dbg_reg_def[regno].offset, +		       dbg_reg_def[regno].size); +	else +		memset(mem, 0, dbg_reg_def[regno].size); +	return dbg_reg_def[regno].name; +} + +int dbg_set_reg(int regno, void *mem, struct pt_regs *regs) +{ +	if (regno >= DBG_MAX_REG_NUM || regno < 0) +		return -EINVAL; + +	if (dbg_reg_def[regno].offset != -1) +		memcpy((void *)regs + dbg_reg_def[regno].offset, mem, +		       dbg_reg_def[regno].size); +	return 0; +} + +void +sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *task) +{ +	struct pt_regs *thread_regs; + +	/* Initialize to zero */ +	memset((char *)gdb_regs, 0, NUMREGBYTES); +	thread_regs = task_pt_regs(task); +	memcpy((void *)gdb_regs, (void *)thread_regs->regs, GP_REG_BYTES); +} + +void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) +{ +	regs->pc = pc; +} + +static int compiled_break; + +static void kgdb_arch_update_addr(struct pt_regs *regs, +				char *remcom_in_buffer) +{ +	unsigned long addr; +	char *ptr; + +	ptr = &remcom_in_buffer[1]; +	if (kgdb_hex2long(&ptr, &addr)) +		kgdb_arch_set_pc(regs, addr); +	else if (compiled_break == 1) +		kgdb_arch_set_pc(regs, regs->pc + 4); + +	compiled_break = 0; +} + +int kgdb_arch_handle_exception(int exception_vector, int signo, +			       int err_code, char *remcom_in_buffer, +			       char *remcom_out_buffer, +			       struct pt_regs *linux_regs) +{ +	int err; + +	switch (remcom_in_buffer[0]) { +	case 'D': +	case 'k': +		/* +		 * Packet D (Detach), k (kill). No special handling +		 * is required here. Handle same as c packet. +		 */ +	case 'c': +		/* +		 * Packet c (Continue) to continue executing. +		 * Set pc to required address. +		 * Try to read optional parameter and set pc. +		 * If this was a compiled breakpoint, we need to move +		 * to the next instruction else we will just breakpoint +		 * over and over again. +		 */ +		kgdb_arch_update_addr(linux_regs, remcom_in_buffer); +		atomic_set(&kgdb_cpu_doing_single_step, -1); +		kgdb_single_step =  0; + +		/* +		 * Received continue command, disable single step +		 */ +		if (kernel_active_single_step()) +			kernel_disable_single_step(); + +		err = 0; +		break; +	case 's': +		/* +		 * Update step address value with address passed +		 * with step packet. +		 * On debug exception return PC is copied to ELR +		 * So just update PC. +		 * If no step address is passed, resume from the address +		 * pointed by PC. Do not update PC +		 */ +		kgdb_arch_update_addr(linux_regs, remcom_in_buffer); +		atomic_set(&kgdb_cpu_doing_single_step, raw_smp_processor_id()); +		kgdb_single_step =  1; + +		/* +		 * Enable single step handling +		 */ +		if (!kernel_active_single_step()) +			kernel_enable_single_step(linux_regs); +		err = 0; +		break; +	default: +		err = -1; +	} +	return err; +} + +static int kgdb_brk_fn(struct pt_regs *regs, unsigned int esr) +{ +	kgdb_handle_exception(1, SIGTRAP, 0, regs); +	return 0; +} + +static int kgdb_compiled_brk_fn(struct pt_regs *regs, unsigned int esr) +{ +	compiled_break = 1; +	kgdb_handle_exception(1, SIGTRAP, 0, regs); + +	return 0; +} + +static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned int esr) +{ +	kgdb_handle_exception(1, SIGTRAP, 0, regs); +	return 0; +} + +static struct break_hook kgdb_brkpt_hook = { +	.esr_mask	= 0xffffffff, +	.esr_val	= DBG_ESR_VAL_BRK(KGDB_DYN_DGB_BRK_IMM), +	.fn		= kgdb_brk_fn +}; + +static struct break_hook kgdb_compiled_brkpt_hook = { +	.esr_mask	= 0xffffffff, +	.esr_val	= DBG_ESR_VAL_BRK(KDBG_COMPILED_DBG_BRK_IMM), +	.fn		= kgdb_compiled_brk_fn +}; + +static struct step_hook kgdb_step_hook = { +	.fn		= kgdb_step_brk_fn +}; + +static void kgdb_call_nmi_hook(void *ignored) +{ +	kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs()); +} + +void kgdb_roundup_cpus(unsigned long flags) +{ +	local_irq_enable(); +	smp_call_function(kgdb_call_nmi_hook, NULL, 0); +	local_irq_disable(); +} + +static int __kgdb_notify(struct die_args *args, unsigned long cmd) +{ +	struct pt_regs *regs = args->regs; + +	if (kgdb_handle_exception(1, args->signr, cmd, regs)) +		return NOTIFY_DONE; +	return NOTIFY_STOP; +} + +static int +kgdb_notify(struct notifier_block *self, unsigned long cmd, void *ptr) +{ +	unsigned long flags; +	int ret; + +	local_irq_save(flags); +	ret = __kgdb_notify(ptr, cmd); +	local_irq_restore(flags); + +	return ret; +} + +static struct notifier_block kgdb_notifier = { +	.notifier_call	= kgdb_notify, +	/* +	 * Want to be lowest priority +	 */ +	.priority	= -INT_MAX, +}; + +/* + * kgdb_arch_init - Perform any architecture specific initalization. + * This function will handle the initalization of any architecture + * specific callbacks. + */ +int kgdb_arch_init(void) +{ +	int ret = register_die_notifier(&kgdb_notifier); + +	if (ret != 0) +		return ret; + +	register_break_hook(&kgdb_brkpt_hook); +	register_break_hook(&kgdb_compiled_brkpt_hook); +	register_step_hook(&kgdb_step_hook); +	return 0; +} + +/* + * kgdb_arch_exit - Perform any architecture specific uninitalization. + * This function will handle the uninitalization of any architecture + * specific callbacks, for dynamic registration and unregistration. + */ +void kgdb_arch_exit(void) +{ +	unregister_break_hook(&kgdb_brkpt_hook); +	unregister_break_hook(&kgdb_compiled_brkpt_hook); +	unregister_step_hook(&kgdb_step_hook); +	unregister_die_notifier(&kgdb_notifier); +} + +/* + * ARM instructions are always in LE. + * Break instruction is encoded in LE format + */ +struct kgdb_arch arch_kgdb_ops = { +	.gdb_bpt_instr = { +		KGDB_DYN_BRK_INS_BYTE0, +		KGDB_DYN_BRK_INS_BYTE1, +		KGDB_DYN_BRK_INS_BYTE2, +		KGDB_DYN_BRK_INS_BYTE3, +	} +}; diff --git a/arch/arm64/kernel/kuser32.S b/arch/arm64/kernel/kuser32.S index 8b69ecb1d8b..7787208e8cc 100644 --- a/arch/arm64/kernel/kuser32.S +++ b/arch/arm64/kernel/kuser32.S @@ -27,6 +27,9 @@   *   * See Documentation/arm/kernel_user_helpers.txt for formal definitions.   */ + +#include <asm/unistd32.h> +  	.align	5  	.globl	__kuser_helper_start  __kuser_helper_start: @@ -35,33 +38,32 @@ __kuser_cmpxchg64:			// 0xffff0f60  	.inst	0xe92d00f0		//	push		{r4, r5, r6, r7}  	.inst	0xe1c040d0		//	ldrd		r4, r5, [r0]  	.inst	0xe1c160d0		//	ldrd		r6, r7, [r1] -	.inst	0xf57ff05f		//	dmb		sy  	.inst	0xe1b20f9f		// 1:	ldrexd		r0, r1, [r2]  	.inst	0xe0303004		//	eors		r3, r0, r4  	.inst	0x00313005		//	eoreqs		r3, r1, r5 -	.inst	0x01a23f96		//	strexdeq	r3, r6, [r2] +	.inst	0x01a23e96		//	stlexdeq	r3, r6, [r2]  	.inst	0x03330001		//	teqeq		r3, #1  	.inst	0x0afffff9		//	beq		1b -	.inst	0xf57ff05f		//	dmb		sy +	.inst	0xf57ff05b		//	dmb		ish  	.inst	0xe2730000		//	rsbs		r0, r3, #0  	.inst	0xe8bd00f0		//	pop		{r4, r5, r6, r7}  	.inst	0xe12fff1e		//	bx		lr  	.align	5  __kuser_memory_barrier:			// 0xffff0fa0 -	.inst	0xf57ff05f		//	dmb		sy +	.inst	0xf57ff05b		//	dmb		ish  	.inst	0xe12fff1e		//	bx		lr  	.align	5  __kuser_cmpxchg:			// 0xffff0fc0 -	.inst	0xf57ff05f		//	dmb		sy  	.inst	0xe1923f9f		// 1:	ldrex		r3, [r2]  	.inst	0xe0533000		//	subs		r3, r3, r0 -	.inst	0x01823f91		//	strexeq	r3, r1, [r2] +	.inst	0x01823e91		//	stlexeq		r3, r1, [r2]  	.inst	0x03330001		//	teqeq		r3, #1  	.inst	0x0afffffa		//	beq		1b +	.inst	0xf57ff05b		//	dmb		ish  	.inst	0xe2730000		//	rsbs		r0, r3, #0 -	.inst	0xeaffffef		//	b		<__kuser_memory_barrier> +	.inst	0xe12fff1e		//	bx		lr  	.align	5  __kuser_get_tls:			// 0xffff0fe0 @@ -75,3 +77,42 @@ __kuser_helper_version:			// 0xffff0ffc  	.word	((__kuser_helper_end - __kuser_helper_start) >> 5)  	.globl	__kuser_helper_end  __kuser_helper_end: + +/* + * AArch32 sigreturn code + * + * For ARM syscalls, the syscall number has to be loaded into r7. + * We do not support an OABI userspace. + * + * For Thumb syscalls, we also pass the syscall number via r7. We therefore + * need two 16-bit instructions. + */ +	.globl __aarch32_sigret_code_start +__aarch32_sigret_code_start: + +	/* +	 * ARM Code +	 */ +	.byte	__NR_compat_sigreturn, 0x70, 0xa0, 0xe3	// mov	r7, #__NR_compat_sigreturn +	.byte	__NR_compat_sigreturn, 0x00, 0x00, 0xef	// svc	#__NR_compat_sigreturn + +	/* +	 * Thumb code +	 */ +	.byte	__NR_compat_sigreturn, 0x27			// svc	#__NR_compat_sigreturn +	.byte	__NR_compat_sigreturn, 0xdf			// mov	r7, #__NR_compat_sigreturn + +	/* +	 * ARM code +	 */ +	.byte	__NR_compat_rt_sigreturn, 0x70, 0xa0, 0xe3	// mov	r7, #__NR_compat_rt_sigreturn +	.byte	__NR_compat_rt_sigreturn, 0x00, 0x00, 0xef	// svc	#__NR_compat_rt_sigreturn + +	/* +	 * Thumb code +	 */ +	.byte	__NR_compat_rt_sigreturn, 0x27			// svc	#__NR_compat_rt_sigreturn +	.byte	__NR_compat_rt_sigreturn, 0xdf			// mov	r7, #__NR_compat_rt_sigreturn + +        .globl __aarch32_sigret_code_end +__aarch32_sigret_code_end: diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c index ca0e3d55da9..1eb1cc95513 100644 --- a/arch/arm64/kernel/module.c +++ b/arch/arm64/kernel/module.c @@ -25,11 +25,15 @@  #include <linux/mm.h>  #include <linux/moduleloader.h>  #include <linux/vmalloc.h> +#include <asm/insn.h> + +#define	AARCH64_INSN_IMM_MOVNZ		AARCH64_INSN_IMM_MAX +#define	AARCH64_INSN_IMM_MOVK		AARCH64_INSN_IMM_16  void *module_alloc(unsigned long size)  {  	return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END, -				    GFP_KERNEL, PAGE_KERNEL_EXEC, -1, +				    GFP_KERNEL, PAGE_KERNEL_EXEC, NUMA_NO_NODE,  				    __builtin_return_address(0));  } @@ -94,25 +98,18 @@ static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len)  	return 0;  } -enum aarch64_imm_type { -	INSN_IMM_MOVNZ, -	INSN_IMM_MOVK, -	INSN_IMM_ADR, -	INSN_IMM_26, -	INSN_IMM_19, -	INSN_IMM_16, -	INSN_IMM_14, -	INSN_IMM_12, -	INSN_IMM_9, -}; - -static u32 encode_insn_immediate(enum aarch64_imm_type type, u32 insn, u64 imm) +static int reloc_insn_movw(enum aarch64_reloc_op op, void *place, u64 val, +			   int lsb, enum aarch64_insn_imm_type imm_type)  { -	u32 immlo, immhi, lomask, himask, mask; -	int shift; +	u64 imm, limit = 0; +	s64 sval; +	u32 insn = le32_to_cpu(*(u32 *)place); + +	sval = do_reloc(op, place, val); +	sval >>= lsb; +	imm = sval & 0xffff; -	switch (type) { -	case INSN_IMM_MOVNZ: +	if (imm_type == AARCH64_INSN_IMM_MOVNZ) {  		/*  		 * For signed MOVW relocations, we have to manipulate the  		 * instruction encoding depending on whether or not the @@ -131,70 +128,12 @@ static u32 encode_insn_immediate(enum aarch64_imm_type type, u32 insn, u64 imm)  			 */  			imm = ~imm;  		} -	case INSN_IMM_MOVK: -		mask = BIT(16) - 1; -		shift = 5; -		break; -	case INSN_IMM_ADR: -		lomask = 0x3; -		himask = 0x7ffff; -		immlo = imm & lomask; -		imm >>= 2; -		immhi = imm & himask; -		imm = (immlo << 24) | (immhi); -		mask = (lomask << 24) | (himask); -		shift = 5; -		break; -	case INSN_IMM_26: -		mask = BIT(26) - 1; -		shift = 0; -		break; -	case INSN_IMM_19: -		mask = BIT(19) - 1; -		shift = 5; -		break; -	case INSN_IMM_16: -		mask = BIT(16) - 1; -		shift = 5; -		break; -	case INSN_IMM_14: -		mask = BIT(14) - 1; -		shift = 5; -		break; -	case INSN_IMM_12: -		mask = BIT(12) - 1; -		shift = 10; -		break; -	case INSN_IMM_9: -		mask = BIT(9) - 1; -		shift = 12; -		break; -	default: -		pr_err("encode_insn_immediate: unknown immediate encoding %d\n", -			type); -		return 0; +		imm_type = AARCH64_INSN_IMM_MOVK;  	} -	/* Update the immediate field. */ -	insn &= ~(mask << shift); -	insn |= (imm & mask) << shift; - -	return insn; -} - -static int reloc_insn_movw(enum aarch64_reloc_op op, void *place, u64 val, -			   int lsb, enum aarch64_imm_type imm_type) -{ -	u64 imm, limit = 0; -	s64 sval; -	u32 insn = *(u32 *)place; - -	sval = do_reloc(op, place, val); -	sval >>= lsb; -	imm = sval & 0xffff; -  	/* Update the instruction with the new encoding. */ -	*(u32 *)place = encode_insn_immediate(imm_type, insn, imm); +	insn = aarch64_insn_encode_immediate(imm_type, insn, imm); +	*(u32 *)place = cpu_to_le32(insn);  	/* Shift out the immediate field. */  	sval >>= 16; @@ -203,9 +142,9 @@ static int reloc_insn_movw(enum aarch64_reloc_op op, void *place, u64 val,  	 * For unsigned immediates, the overflow check is straightforward.  	 * For signed immediates, the sign bit is actually the bit past the  	 * most significant bit of the field. -	 * The INSN_IMM_16 immediate type is unsigned. +	 * The AARCH64_INSN_IMM_16 immediate type is unsigned.  	 */ -	if (imm_type != INSN_IMM_16) { +	if (imm_type != AARCH64_INSN_IMM_16) {  		sval++;  		limit++;  	} @@ -218,11 +157,11 @@ static int reloc_insn_movw(enum aarch64_reloc_op op, void *place, u64 val,  }  static int reloc_insn_imm(enum aarch64_reloc_op op, void *place, u64 val, -			  int lsb, int len, enum aarch64_imm_type imm_type) +			  int lsb, int len, enum aarch64_insn_imm_type imm_type)  {  	u64 imm, imm_mask;  	s64 sval; -	u32 insn = *(u32 *)place; +	u32 insn = le32_to_cpu(*(u32 *)place);  	/* Calculate the relocation value. */  	sval = do_reloc(op, place, val); @@ -233,7 +172,8 @@ static int reloc_insn_imm(enum aarch64_reloc_op op, void *place, u64 val,  	imm = sval & imm_mask;  	/* Update the instruction's immediate field. */ -	*(u32 *)place = encode_insn_immediate(imm_type, insn, imm); +	insn = aarch64_insn_encode_immediate(imm_type, insn, imm); +	*(u32 *)place = cpu_to_le32(insn);  	/*  	 * Extract the upper value bits (including the sign bit) and @@ -315,125 +255,125 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,  			overflow_check = false;  		case R_AARCH64_MOVW_UABS_G0:  			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0, -					      INSN_IMM_16); +					      AARCH64_INSN_IMM_16);  			break;  		case R_AARCH64_MOVW_UABS_G1_NC:  			overflow_check = false;  		case R_AARCH64_MOVW_UABS_G1:  			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16, -					      INSN_IMM_16); +					      AARCH64_INSN_IMM_16);  			break;  		case R_AARCH64_MOVW_UABS_G2_NC:  			overflow_check = false;  		case R_AARCH64_MOVW_UABS_G2:  			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32, -					      INSN_IMM_16); +					      AARCH64_INSN_IMM_16);  			break;  		case R_AARCH64_MOVW_UABS_G3:  			/* We're using the top bits so we can't overflow. */  			overflow_check = false;  			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 48, -					      INSN_IMM_16); +					      AARCH64_INSN_IMM_16);  			break;  		case R_AARCH64_MOVW_SABS_G0:  			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0, -					      INSN_IMM_MOVNZ); +					      AARCH64_INSN_IMM_MOVNZ);  			break;  		case R_AARCH64_MOVW_SABS_G1:  			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16, -					      INSN_IMM_MOVNZ); +					      AARCH64_INSN_IMM_MOVNZ);  			break;  		case R_AARCH64_MOVW_SABS_G2:  			ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32, -					      INSN_IMM_MOVNZ); +					      AARCH64_INSN_IMM_MOVNZ);  			break;  		case R_AARCH64_MOVW_PREL_G0_NC:  			overflow_check = false;  			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0, -					      INSN_IMM_MOVK); +					      AARCH64_INSN_IMM_MOVK);  			break;  		case R_AARCH64_MOVW_PREL_G0:  			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0, -					      INSN_IMM_MOVNZ); +					      AARCH64_INSN_IMM_MOVNZ);  			break;  		case R_AARCH64_MOVW_PREL_G1_NC:  			overflow_check = false;  			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16, -					      INSN_IMM_MOVK); +					      AARCH64_INSN_IMM_MOVK);  			break;  		case R_AARCH64_MOVW_PREL_G1:  			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16, -					      INSN_IMM_MOVNZ); +					      AARCH64_INSN_IMM_MOVNZ);  			break;  		case R_AARCH64_MOVW_PREL_G2_NC:  			overflow_check = false;  			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32, -					      INSN_IMM_MOVK); +					      AARCH64_INSN_IMM_MOVK);  			break;  		case R_AARCH64_MOVW_PREL_G2:  			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32, -					      INSN_IMM_MOVNZ); +					      AARCH64_INSN_IMM_MOVNZ);  			break;  		case R_AARCH64_MOVW_PREL_G3:  			/* We're using the top bits so we can't overflow. */  			overflow_check = false;  			ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 48, -					      INSN_IMM_MOVNZ); +					      AARCH64_INSN_IMM_MOVNZ);  			break;  		/* Immediate instruction relocations. */  		case R_AARCH64_LD_PREL_LO19:  			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19, -					     INSN_IMM_19); +					     AARCH64_INSN_IMM_19);  			break;  		case R_AARCH64_ADR_PREL_LO21:  			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 0, 21, -					     INSN_IMM_ADR); +					     AARCH64_INSN_IMM_ADR);  			break;  		case R_AARCH64_ADR_PREL_PG_HI21_NC:  			overflow_check = false;  		case R_AARCH64_ADR_PREL_PG_HI21:  			ovf = reloc_insn_imm(RELOC_OP_PAGE, loc, val, 12, 21, -					     INSN_IMM_ADR); +					     AARCH64_INSN_IMM_ADR);  			break;  		case R_AARCH64_ADD_ABS_LO12_NC:  		case R_AARCH64_LDST8_ABS_LO12_NC:  			overflow_check = false;  			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 0, 12, -					     INSN_IMM_12); +					     AARCH64_INSN_IMM_12);  			break;  		case R_AARCH64_LDST16_ABS_LO12_NC:  			overflow_check = false;  			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 1, 11, -					     INSN_IMM_12); +					     AARCH64_INSN_IMM_12);  			break;  		case R_AARCH64_LDST32_ABS_LO12_NC:  			overflow_check = false;  			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 2, 10, -					     INSN_IMM_12); +					     AARCH64_INSN_IMM_12);  			break;  		case R_AARCH64_LDST64_ABS_LO12_NC:  			overflow_check = false;  			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 3, 9, -					     INSN_IMM_12); +					     AARCH64_INSN_IMM_12);  			break;  		case R_AARCH64_LDST128_ABS_LO12_NC:  			overflow_check = false;  			ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 4, 8, -					     INSN_IMM_12); +					     AARCH64_INSN_IMM_12);  			break;  		case R_AARCH64_TSTBR14:  			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 14, -					     INSN_IMM_14); +					     AARCH64_INSN_IMM_14);  			break;  		case R_AARCH64_CONDBR19:  			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19, -					     INSN_IMM_19); +					     AARCH64_INSN_IMM_19);  			break;  		case R_AARCH64_JUMP26:  		case R_AARCH64_CALL26:  			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26, -					     INSN_IMM_26); +					     AARCH64_INSN_IMM_26);  			break;  		default: diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index cea1594ff93..baf5afb7e6a 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c @@ -22,6 +22,7 @@  #include <linux/bitmap.h>  #include <linux/interrupt.h> +#include <linux/irq.h>  #include <linux/kernel.h>  #include <linux/export.h>  #include <linux/perf_event.h> @@ -363,26 +364,53 @@ validate_group(struct perf_event *event)  }  static void +armpmu_disable_percpu_irq(void *data) +{ +	unsigned int irq = *(unsigned int *)data; +	disable_percpu_irq(irq); +} + +static void  armpmu_release_hardware(struct arm_pmu *armpmu)  { -	int i, irq, irqs; +	int irq; +	unsigned int i, irqs;  	struct platform_device *pmu_device = armpmu->plat_device;  	irqs = min(pmu_device->num_resources, num_possible_cpus()); +	if (!irqs) +		return; -	for (i = 0; i < irqs; ++i) { -		if (!cpumask_test_and_clear_cpu(i, &armpmu->active_irqs)) -			continue; -		irq = platform_get_irq(pmu_device, i); -		if (irq >= 0) -			free_irq(irq, armpmu); +	irq = platform_get_irq(pmu_device, 0); +	if (irq <= 0) +		return; + +	if (irq_is_percpu(irq)) { +		on_each_cpu(armpmu_disable_percpu_irq, &irq, 1); +		free_percpu_irq(irq, &cpu_hw_events); +	} else { +		for (i = 0; i < irqs; ++i) { +			if (!cpumask_test_and_clear_cpu(i, &armpmu->active_irqs)) +				continue; +			irq = platform_get_irq(pmu_device, i); +			if (irq > 0) +				free_irq(irq, armpmu); +		}  	}  } +static void +armpmu_enable_percpu_irq(void *data) +{ +	unsigned int irq = *(unsigned int *)data; +	enable_percpu_irq(irq, IRQ_TYPE_NONE); +} +  static int  armpmu_reserve_hardware(struct arm_pmu *armpmu)  { -	int i, err, irq, irqs; +	int err, irq; +	unsigned int i, irqs;  	struct platform_device *pmu_device = armpmu->plat_device;  	if (!pmu_device) { @@ -391,39 +419,59 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu)  	}  	irqs = min(pmu_device->num_resources, num_possible_cpus()); -	if (irqs < 1) { +	if (!irqs) {  		pr_err("no irqs for PMUs defined\n");  		return -ENODEV;  	} -	for (i = 0; i < irqs; ++i) { -		err = 0; -		irq = platform_get_irq(pmu_device, i); -		if (irq < 0) -			continue; +	irq = platform_get_irq(pmu_device, 0); +	if (irq <= 0) { +		pr_err("failed to get valid irq for PMU device\n"); +		return -ENODEV; +	} -		/* -		 * If we have a single PMU interrupt that we can't shift, -		 * assume that we're running on a uniprocessor machine and -		 * continue. Otherwise, continue without this interrupt. -		 */ -		if (irq_set_affinity(irq, cpumask_of(i)) && irqs > 1) { -			pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n", -				    irq, i); -			continue; -		} +	if (irq_is_percpu(irq)) { +		err = request_percpu_irq(irq, armpmu->handle_irq, +				"arm-pmu", &cpu_hw_events); -		err = request_irq(irq, armpmu->handle_irq, -				  IRQF_NOBALANCING, -				  "arm-pmu", armpmu);  		if (err) { -			pr_err("unable to request IRQ%d for ARM PMU counters\n", -				irq); +			pr_err("unable to request percpu IRQ%d for ARM PMU counters\n", +					irq);  			armpmu_release_hardware(armpmu);  			return err;  		} -		cpumask_set_cpu(i, &armpmu->active_irqs); +		on_each_cpu(armpmu_enable_percpu_irq, &irq, 1); +	} else { +		for (i = 0; i < irqs; ++i) { +			err = 0; +			irq = platform_get_irq(pmu_device, i); +			if (irq <= 0) +				continue; + +			/* +			 * If we have a single PMU interrupt that we can't shift, +			 * assume that we're running on a uniprocessor machine and +			 * continue. Otherwise, continue without this interrupt. +			 */ +			if (irq_set_affinity(irq, cpumask_of(i)) && irqs > 1) { +				pr_warning("unable to set irq affinity (irq=%d, cpu=%u)\n", +						irq, i); +				continue; +			} + +			err = request_irq(irq, armpmu->handle_irq, +					IRQF_NOBALANCING, +					"arm-pmu", armpmu); +			if (err) { +				pr_err("unable to request IRQ%d for ARM PMU counters\n", +						irq); +				armpmu_release_hardware(armpmu); +				return err; +			} + +			cpumask_set_cpu(i, &armpmu->active_irqs); +		}  	}  	return 0; @@ -784,8 +832,8 @@ static const unsigned armv8_pmuv3_perf_cache_map[PERF_COUNT_HW_CACHE_MAX]  /*   * PMXEVTYPER: Event selection reg   */ -#define	ARMV8_EVTYPE_MASK	0xc80000ff	/* Mask for writable bits */ -#define	ARMV8_EVTYPE_EVENT	0xff		/* Mask for EVENT bits */ +#define	ARMV8_EVTYPE_MASK	0xc80003ff	/* Mask for writable bits */ +#define	ARMV8_EVTYPE_EVENT	0x3ff		/* Mask for EVENT bits */  /*   * Event filters for PMUv3 @@ -1044,7 +1092,7 @@ static irqreturn_t armv8pmu_handle_irq(int irq_num, void *dev)  	 */  	regs = get_irq_regs(); -	cpuc = &__get_cpu_var(cpu_hw_events); +	cpuc = this_cpu_ptr(&cpu_hw_events);  	for (idx = 0; idx < cpu_pmu->num_events; ++idx) {  		struct perf_event *event = cpuc->events[idx];  		struct hw_perf_event *hwc; @@ -1175,7 +1223,8 @@ static void armv8pmu_reset(void *info)  static int armv8_pmuv3_map_event(struct perf_event *event)  {  	return map_cpu_event(event, &armv8_pmuv3_perf_map, -				&armv8_pmuv3_perf_cache_map, 0xFF); +				&armv8_pmuv3_perf_cache_map, +				ARMV8_EVTYPE_EVENT);  }  static struct arm_pmu armv8pmu = { @@ -1257,7 +1306,7 @@ device_initcall(register_pmu_driver);  static struct pmu_hw_events *armpmu_get_cpu_events(void)  { -	return &__get_cpu_var(cpu_hw_events); +	return this_cpu_ptr(&cpu_hw_events);  }  static void __init cpu_pmu_init(struct arm_pmu *armpmu) @@ -1299,8 +1348,8 @@ early_initcall(init_hw_perf_events);   * Callchain handling code.   */  struct frame_tail { -	struct frame_tail   __user *fp; -	unsigned long	    lr; +	struct frame_tail	__user *fp; +	unsigned long		lr;  } __attribute__((packed));  /* @@ -1337,22 +1386,84 @@ user_backtrace(struct frame_tail __user *tail,  	return buftail.fp;  } +#ifdef CONFIG_COMPAT +/* + * The registers we're interested in are at the end of the variable + * length saved register structure. The fp points at the end of this + * structure so the address of this struct is: + * (struct compat_frame_tail *)(xxx->fp)-1 + * + * This code has been adapted from the ARM OProfile support. + */ +struct compat_frame_tail { +	compat_uptr_t	fp; /* a (struct compat_frame_tail *) in compat mode */ +	u32		sp; +	u32		lr; +} __attribute__((packed)); + +static struct compat_frame_tail __user * +compat_user_backtrace(struct compat_frame_tail __user *tail, +		      struct perf_callchain_entry *entry) +{ +	struct compat_frame_tail buftail; +	unsigned long err; + +	/* Also check accessibility of one struct frame_tail beyond */ +	if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) +		return NULL; + +	pagefault_disable(); +	err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail)); +	pagefault_enable(); + +	if (err) +		return NULL; + +	perf_callchain_store(entry, buftail.lr); + +	/* +	 * Frame pointers should strictly progress back up the stack +	 * (towards higher addresses). +	 */ +	if (tail + 1 >= (struct compat_frame_tail __user *) +			compat_ptr(buftail.fp)) +		return NULL; + +	return (struct compat_frame_tail __user *)compat_ptr(buftail.fp) - 1; +} +#endif /* CONFIG_COMPAT */ +  void perf_callchain_user(struct perf_callchain_entry *entry,  			 struct pt_regs *regs)  { -	struct frame_tail __user *tail; -  	if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {  		/* We don't support guest os callchain now */  		return;  	}  	perf_callchain_store(entry, regs->pc); -	tail = (struct frame_tail __user *)regs->regs[29]; -	while (entry->nr < PERF_MAX_STACK_DEPTH && -	       tail && !((unsigned long)tail & 0xf)) -		tail = user_backtrace(tail, entry); +	if (!compat_user_mode(regs)) { +		/* AARCH64 mode */ +		struct frame_tail __user *tail; + +		tail = (struct frame_tail __user *)regs->regs[29]; + +		while (entry->nr < PERF_MAX_STACK_DEPTH && +		       tail && !((unsigned long)tail & 0xf)) +			tail = user_backtrace(tail, entry); +	} else { +#ifdef CONFIG_COMPAT +		/* AARCH32 compat mode */ +		struct compat_frame_tail __user *tail; + +		tail = (struct compat_frame_tail __user *)regs->compat_fp - 1; + +		while ((entry->nr < PERF_MAX_STACK_DEPTH) && +			tail && !((unsigned long)tail & 0x3)) +			tail = compat_user_backtrace(tail, entry); +#endif +	}  }  /* @@ -1380,6 +1491,7 @@ void perf_callchain_kernel(struct perf_callchain_entry *entry,  	frame.fp = regs->regs[29];  	frame.sp = regs->sp;  	frame.pc = regs->pc; +  	walk_stackframe(&frame, callchain_trace, entry);  } diff --git a/arch/arm64/kernel/perf_regs.c b/arch/arm64/kernel/perf_regs.c new file mode 100644 index 00000000000..422ebd63b61 --- /dev/null +++ b/arch/arm64/kernel/perf_regs.c @@ -0,0 +1,46 @@ +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/perf_event.h> +#include <linux/bug.h> + +#include <asm/compat.h> +#include <asm/perf_regs.h> +#include <asm/ptrace.h> + +u64 perf_reg_value(struct pt_regs *regs, int idx) +{ +	if (WARN_ON_ONCE((u32)idx >= PERF_REG_ARM64_MAX)) +		return 0; + +	/* +	 * Compat (i.e. 32 bit) mode: +	 * - PC has been set in the pt_regs struct in kernel_entry, +	 * - Handle SP and LR here. +	 */ +	if (compat_user_mode(regs)) { +		if ((u32)idx == PERF_REG_ARM64_SP) +			return regs->compat_sp; +		if ((u32)idx == PERF_REG_ARM64_LR) +			return regs->compat_lr; +	} + +	return regs->regs[idx]; +} + +#define REG_RESERVED (~((1ULL << PERF_REG_ARM64_MAX) - 1)) + +int perf_reg_validate(u64 mask) +{ +	if (!mask || mask & REG_RESERVED) +		return -EINVAL; + +	return 0; +} + +u64 perf_reg_abi(struct task_struct *task) +{ +	if (is_compat_thread(task_thread_info(task))) +		return PERF_SAMPLE_REGS_ABI_32; +	else +		return PERF_SAMPLE_REGS_ABI_64; +} diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index 7ae8a1f00c3..43b7c34f92c 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -20,6 +20,7 @@  #include <stdarg.h> +#include <linux/compat.h>  #include <linux/export.h>  #include <linux/sched.h>  #include <linux/kernel.h> @@ -71,8 +72,17 @@ static void setup_restart(void)  void soft_restart(unsigned long addr)  { +	typedef void (*phys_reset_t)(unsigned long); +	phys_reset_t phys_reset; +  	setup_restart(); -	cpu_reset(addr); + +	/* Switch to the identity mapping */ +	phys_reset = (phys_reset_t)virt_to_phys(cpu_reset); +	phys_reset(addr); + +	/* Should never get here */ +	BUG();  }  /* @@ -84,11 +94,6 @@ EXPORT_SYMBOL_GPL(pm_power_off);  void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd);  EXPORT_SYMBOL_GPL(arm_pm_restart); -void arch_cpu_idle_prepare(void) -{ -	local_fiq_enable(); -} -  /*   * This is our default idle handler.   */ @@ -102,33 +107,69 @@ void arch_cpu_idle(void)  	local_irq_enable();  } -void machine_shutdown(void) +#ifdef CONFIG_HOTPLUG_CPU +void arch_cpu_idle_dead(void)  { -#ifdef CONFIG_SMP -	smp_send_stop(); +       cpu_die(); +}  #endif + +/* + * Called by kexec, immediately prior to machine_kexec(). + * + * This must completely disable all secondary CPUs; simply causing those CPUs + * to execute e.g. a RAM-based pin loop is not sufficient. This allows the + * kexec'd kernel to use any and all RAM as it sees fit, without having to + * avoid any code or data used by any SW CPU pin loop. The CPU hotplug + * functionality embodied in disable_nonboot_cpus() to achieve this. + */ +void machine_shutdown(void) +{ +	disable_nonboot_cpus();  } +/* + * Halting simply requires that the secondary CPUs stop performing any + * activity (executing tasks, handling interrupts). smp_send_stop() + * achieves this. + */  void machine_halt(void)  { -	machine_shutdown(); +	local_irq_disable(); +	smp_send_stop();  	while (1);  } +/* + * Power-off simply requires that the secondary CPUs stop performing any + * activity (executing tasks, handling interrupts). smp_send_stop() + * achieves this. When the system power is turned off, it will take all CPUs + * with it. + */  void machine_power_off(void)  { -	machine_shutdown(); +	local_irq_disable(); +	smp_send_stop();  	if (pm_power_off)  		pm_power_off();  } +/* + * Restart requires that the secondary CPUs stop performing any activity + * while the primary CPU resets the system. Systems with a single CPU can + * use soft_restart() as their machine descriptor's .restart hook, since that + * will cause the only available CPU to reset. Systems with multiple CPUs must + * provide a HW restart implementation, to ensure that all CPUs reset at once. + * This is required so that any code running after reset on the primary CPU + * doesn't have to co-ordinate with other CPUs to ensure they aren't still + * executing pre-reset code, and using RAM that the primary CPU's code wishes + * to use. Implementing such co-ordination would be essentially impossible. + */  void machine_restart(char *cmd)  { -	machine_shutdown(); -  	/* Disable interrupts first */  	local_irq_disable(); -	local_fiq_disable(); +	smp_send_stop();  	/* Now call the architecture specific reboot code. */  	if (arm_pm_restart) @@ -195,7 +236,7 @@ void release_thread(struct task_struct *dead_task)  int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)  { -	fpsimd_save_state(¤t->thread.fpsimd_state); +	fpsimd_preserve_current_state();  	*dst = *src;  	return 0;  } @@ -290,7 +331,7 @@ struct task_struct *__switch_to(struct task_struct *prev,  	 * Complete any pending TLB or cache maintenance on this CPU in case  	 * the thread migrates to a different CPU.  	 */ -	dsb(); +	dsb(ish);  	/* the actual thread switch */  	last = cpu_switch_to(prev, next); @@ -301,6 +342,7 @@ struct task_struct *__switch_to(struct task_struct *prev,  unsigned long get_wchan(struct task_struct *p)  {  	struct stackframe frame; +	unsigned long stack_page;  	int count = 0;  	if (!p || p == current || p->state == TASK_RUNNING)  		return 0; @@ -308,9 +350,11 @@ unsigned long get_wchan(struct task_struct *p)  	frame.fp = thread_saved_fp(p);  	frame.sp = thread_saved_sp(p);  	frame.pc = thread_saved_pc(p); +	stack_page = (unsigned long)task_stack_page(p);  	do { -		int ret = unwind_frame(&frame); -		if (ret < 0) +		if (frame.sp < stack_page || +		    frame.sp >= stack_page + THREAD_SIZE || +		    unwind_frame(&frame))  			return 0;  		if (!in_sched_functions(frame.pc))  			return frame.pc; diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c index 14f73c445ff..9e9798f9117 100644 --- a/arch/arm64/kernel/psci.c +++ b/arch/arm64/kernel/psci.c @@ -17,61 +17,80 @@  #include <linux/init.h>  #include <linux/of.h> +#include <linux/smp.h> +#include <linux/reboot.h> +#include <linux/pm.h> +#include <linux/delay.h> +#include <uapi/linux/psci.h>  #include <asm/compiler.h> +#include <asm/cpu_ops.h>  #include <asm/errno.h>  #include <asm/psci.h> +#include <asm/smp_plat.h> +#include <asm/system_misc.h> -struct psci_operations psci_ops; +#define PSCI_POWER_STATE_TYPE_STANDBY		0 +#define PSCI_POWER_STATE_TYPE_POWER_DOWN	1 + +struct psci_power_state { +	u16	id; +	u8	type; +	u8	affinity_level; +}; + +struct psci_operations { +	int (*cpu_suspend)(struct psci_power_state state, +			   unsigned long entry_point); +	int (*cpu_off)(struct psci_power_state state); +	int (*cpu_on)(unsigned long cpuid, unsigned long entry_point); +	int (*migrate)(unsigned long cpuid); +	int (*affinity_info)(unsigned long target_affinity, +			unsigned long lowest_affinity_level); +	int (*migrate_info_type)(void); +}; + +static struct psci_operations psci_ops;  static int (*invoke_psci_fn)(u64, u64, u64, u64); +typedef int (*psci_initcall_t)(const struct device_node *);  enum psci_function {  	PSCI_FN_CPU_SUSPEND,  	PSCI_FN_CPU_ON,  	PSCI_FN_CPU_OFF,  	PSCI_FN_MIGRATE, +	PSCI_FN_AFFINITY_INFO, +	PSCI_FN_MIGRATE_INFO_TYPE,  	PSCI_FN_MAX,  };  static u32 psci_function_id[PSCI_FN_MAX]; -#define PSCI_RET_SUCCESS		0 -#define PSCI_RET_EOPNOTSUPP		-1 -#define PSCI_RET_EINVAL			-2 -#define PSCI_RET_EPERM			-3 -  static int psci_to_linux_errno(int errno)  {  	switch (errno) {  	case PSCI_RET_SUCCESS:  		return 0; -	case PSCI_RET_EOPNOTSUPP: +	case PSCI_RET_NOT_SUPPORTED:  		return -EOPNOTSUPP; -	case PSCI_RET_EINVAL: +	case PSCI_RET_INVALID_PARAMS:  		return -EINVAL; -	case PSCI_RET_EPERM: +	case PSCI_RET_DENIED:  		return -EPERM;  	};  	return -EINVAL;  } -#define PSCI_POWER_STATE_ID_MASK	0xffff -#define PSCI_POWER_STATE_ID_SHIFT	0 -#define PSCI_POWER_STATE_TYPE_MASK	0x1 -#define PSCI_POWER_STATE_TYPE_SHIFT	16 -#define PSCI_POWER_STATE_AFFL_MASK	0x3 -#define PSCI_POWER_STATE_AFFL_SHIFT	24 -  static u32 psci_power_state_pack(struct psci_power_state state)  { -	return	((state.id & PSCI_POWER_STATE_ID_MASK) -			<< PSCI_POWER_STATE_ID_SHIFT)	| -		((state.type & PSCI_POWER_STATE_TYPE_MASK) -			<< PSCI_POWER_STATE_TYPE_SHIFT)	| -		((state.affinity_level & PSCI_POWER_STATE_AFFL_MASK) -			<< PSCI_POWER_STATE_AFFL_SHIFT); +	return ((state.id << PSCI_0_2_POWER_STATE_ID_SHIFT) +			& PSCI_0_2_POWER_STATE_ID_MASK) | +		((state.type << PSCI_0_2_POWER_STATE_TYPE_SHIFT) +		 & PSCI_0_2_POWER_STATE_TYPE_MASK) | +		((state.affinity_level << PSCI_0_2_POWER_STATE_AFFL_SHIFT) +		 & PSCI_0_2_POWER_STATE_AFFL_MASK);  }  /* @@ -108,6 +127,14 @@ static noinline int __invoke_psci_fn_smc(u64 function_id, u64 arg0, u64 arg1,  	return function_id;  } +static int psci_get_version(void) +{ +	int err; + +	err = invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0); +	return err; +} +  static int psci_cpu_suspend(struct psci_power_state state,  			    unsigned long entry_point)  { @@ -151,28 +178,36 @@ static int psci_migrate(unsigned long cpuid)  	return psci_to_linux_errno(err);  } -static const struct of_device_id psci_of_match[] __initconst = { -	{ .compatible = "arm,psci",	}, -	{}, -}; +static int psci_affinity_info(unsigned long target_affinity, +		unsigned long lowest_affinity_level) +{ +	int err; +	u32 fn; -int __init psci_init(void) +	fn = psci_function_id[PSCI_FN_AFFINITY_INFO]; +	err = invoke_psci_fn(fn, target_affinity, lowest_affinity_level, 0); +	return err; +} + +static int psci_migrate_info_type(void)  { -	struct device_node *np; -	const char *method; -	u32 id; -	int err = 0; +	int err; +	u32 fn; -	np = of_find_matching_node(NULL, psci_of_match); -	if (!np) -		return -ENODEV; +	fn = psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE]; +	err = invoke_psci_fn(fn, 0, 0, 0); +	return err; +} + +static int get_set_conduit_method(struct device_node *np) +{ +	const char *method; -	pr_info("probing function IDs from device-tree\n"); +	pr_info("probing for conduit method from DT.\n");  	if (of_property_read_string(np, "method", &method)) { -		pr_warning("missing \"method\" property\n"); -		err = -ENXIO; -		goto out_put_node; +		pr_warn("missing \"method\" property\n"); +		return -ENXIO;  	}  	if (!strcmp("hvc", method)) { @@ -180,11 +215,99 @@ int __init psci_init(void)  	} else if (!strcmp("smc", method)) {  		invoke_psci_fn = __invoke_psci_fn_smc;  	} else { -		pr_warning("invalid \"method\" property: %s\n", method); -		err = -EINVAL; +		pr_warn("invalid \"method\" property: %s\n", method); +		return -EINVAL; +	} +	return 0; +} + +static void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd) +{ +	invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0); +} + +static void psci_sys_poweroff(void) +{ +	invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); +} + +/* + * PSCI Function IDs for v0.2+ are well defined so use + * standard values. + */ +static int psci_0_2_init(struct device_node *np) +{ +	int err, ver; + +	err = get_set_conduit_method(np); + +	if (err)  		goto out_put_node; + +	ver = psci_get_version(); + +	if (ver == PSCI_RET_NOT_SUPPORTED) { +		/* PSCI v0.2 mandates implementation of PSCI_ID_VERSION. */ +		pr_err("PSCI firmware does not comply with the v0.2 spec.\n"); +		err = -EOPNOTSUPP; +		goto out_put_node; +	} else { +		pr_info("PSCIv%d.%d detected in firmware.\n", +				PSCI_VERSION_MAJOR(ver), +				PSCI_VERSION_MINOR(ver)); + +		if (PSCI_VERSION_MAJOR(ver) == 0 && +				PSCI_VERSION_MINOR(ver) < 2) { +			err = -EINVAL; +			pr_err("Conflicting PSCI version detected.\n"); +			goto out_put_node; +		}  	} +	pr_info("Using standard PSCI v0.2 function IDs\n"); +	psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN64_CPU_SUSPEND; +	psci_ops.cpu_suspend = psci_cpu_suspend; + +	psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF; +	psci_ops.cpu_off = psci_cpu_off; + +	psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN64_CPU_ON; +	psci_ops.cpu_on = psci_cpu_on; + +	psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN64_MIGRATE; +	psci_ops.migrate = psci_migrate; + +	psci_function_id[PSCI_FN_AFFINITY_INFO] = PSCI_0_2_FN64_AFFINITY_INFO; +	psci_ops.affinity_info = psci_affinity_info; + +	psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE] = +		PSCI_0_2_FN_MIGRATE_INFO_TYPE; +	psci_ops.migrate_info_type = psci_migrate_info_type; + +	arm_pm_restart = psci_sys_reset; + +	pm_power_off = psci_sys_poweroff; + +out_put_node: +	of_node_put(np); +	return err; +} + +/* + * PSCI < v0.2 get PSCI Function IDs via DT. + */ +static int psci_0_1_init(struct device_node *np) +{ +	u32 id; +	int err; + +	err = get_set_conduit_method(np); + +	if (err) +		goto out_put_node; + +	pr_info("Using PSCI v0.1 Function IDs from DT\n"); +  	if (!of_property_read_u32(np, "cpu_suspend", &id)) {  		psci_function_id[PSCI_FN_CPU_SUSPEND] = id;  		psci_ops.cpu_suspend = psci_cpu_suspend; @@ -209,3 +332,119 @@ out_put_node:  	of_node_put(np);  	return err;  } + +static const struct of_device_id psci_of_match[] __initconst = { +	{ .compatible = "arm,psci",	.data = psci_0_1_init}, +	{ .compatible = "arm,psci-0.2",	.data = psci_0_2_init}, +	{}, +}; + +int __init psci_init(void) +{ +	struct device_node *np; +	const struct of_device_id *matched_np; +	psci_initcall_t init_fn; + +	np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np); + +	if (!np) +		return -ENODEV; + +	init_fn = (psci_initcall_t)matched_np->data; +	return init_fn(np); +} + +#ifdef CONFIG_SMP + +static int __init cpu_psci_cpu_init(struct device_node *dn, unsigned int cpu) +{ +	return 0; +} + +static int __init cpu_psci_cpu_prepare(unsigned int cpu) +{ +	if (!psci_ops.cpu_on) { +		pr_err("no cpu_on method, not booting CPU%d\n", cpu); +		return -ENODEV; +	} + +	return 0; +} + +static int cpu_psci_cpu_boot(unsigned int cpu) +{ +	int err = psci_ops.cpu_on(cpu_logical_map(cpu), __pa(secondary_entry)); +	if (err) +		pr_err("failed to boot CPU%d (%d)\n", cpu, err); + +	return err; +} + +#ifdef CONFIG_HOTPLUG_CPU +static int cpu_psci_cpu_disable(unsigned int cpu) +{ +	/* Fail early if we don't have CPU_OFF support */ +	if (!psci_ops.cpu_off) +		return -EOPNOTSUPP; +	return 0; +} + +static void cpu_psci_cpu_die(unsigned int cpu) +{ +	int ret; +	/* +	 * There are no known implementations of PSCI actually using the +	 * power state field, pass a sensible default for now. +	 */ +	struct psci_power_state state = { +		.type = PSCI_POWER_STATE_TYPE_POWER_DOWN, +	}; + +	ret = psci_ops.cpu_off(state); + +	pr_crit("unable to power off CPU%u (%d)\n", cpu, ret); +} + +static int cpu_psci_cpu_kill(unsigned int cpu) +{ +	int err, i; + +	if (!psci_ops.affinity_info) +		return 1; +	/* +	 * cpu_kill could race with cpu_die and we can +	 * potentially end up declaring this cpu undead +	 * while it is dying. So, try again a few times. +	 */ + +	for (i = 0; i < 10; i++) { +		err = psci_ops.affinity_info(cpu_logical_map(cpu), 0); +		if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) { +			pr_info("CPU%d killed.\n", cpu); +			return 1; +		} + +		msleep(10); +		pr_info("Retrying again to check for CPU kill\n"); +	} + +	pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n", +			cpu, err); +	/* Make op_cpu_kill() fail. */ +	return 0; +} +#endif + +const struct cpu_operations cpu_psci_ops = { +	.name		= "psci", +	.cpu_init	= cpu_psci_cpu_init, +	.cpu_prepare	= cpu_psci_cpu_prepare, +	.cpu_boot	= cpu_psci_cpu_boot, +#ifdef CONFIG_HOTPLUG_CPU +	.cpu_disable	= cpu_psci_cpu_disable, +	.cpu_die	= cpu_psci_cpu_die, +	.cpu_kill	= cpu_psci_cpu_kill, +#endif +}; + +#endif diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index fecdbf7de82..9fde010c945 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -19,6 +19,7 @@   * along with this program.  If not, see <http://www.gnu.org/licenses/>.   */ +#include <linux/compat.h>  #include <linux/kernel.h>  #include <linux/sched.h>  #include <linux/mm.h> @@ -41,6 +42,9 @@  #include <asm/traps.h>  #include <asm/system_misc.h> +#define CREATE_TRACE_POINTS +#include <trace/events/syscalls.h> +  /*   * TODO: does not yet catch signals sent when the child dies.   * in exit.c or in signal.c. @@ -214,31 +218,29 @@ static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type,  {  	int err, len, type, disabled = !ctrl.enabled; -	if (disabled) { -		len = 0; -		type = HW_BREAKPOINT_EMPTY; -	} else { -		err = arch_bp_generic_fields(ctrl, &len, &type); -		if (err) -			return err; - -		switch (note_type) { -		case NT_ARM_HW_BREAK: -			if ((type & HW_BREAKPOINT_X) != type) -				return -EINVAL; -			break; -		case NT_ARM_HW_WATCH: -			if ((type & HW_BREAKPOINT_RW) != type) -				return -EINVAL; -			break; -		default: +	attr->disabled = disabled; +	if (disabled) +		return 0; + +	err = arch_bp_generic_fields(ctrl, &len, &type); +	if (err) +		return err; + +	switch (note_type) { +	case NT_ARM_HW_BREAK: +		if ((type & HW_BREAKPOINT_X) != type)  			return -EINVAL; -		} +		break; +	case NT_ARM_HW_WATCH: +		if ((type & HW_BREAKPOINT_RW) != type) +			return -EINVAL; +		break; +	default: +		return -EINVAL;  	}  	attr->bp_len	= len;  	attr->bp_type	= type; -	attr->disabled	= disabled;  	return 0;  } @@ -519,6 +521,7 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset,  		return ret;  	target->thread.fpsimd_state.user_fpsimd = newstate; +	fpsimd_flush_task_state(target);  	return ret;  } @@ -636,28 +639,32 @@ static int compat_gpr_get(struct task_struct *target,  	for (i = 0; i < num_regs; ++i) {  		unsigned int idx = start + i; -		void *reg; +		compat_ulong_t reg;  		switch (idx) {  		case 15: -			reg = (void *)&task_pt_regs(target)->pc; +			reg = task_pt_regs(target)->pc;  			break;  		case 16: -			reg = (void *)&task_pt_regs(target)->pstate; +			reg = task_pt_regs(target)->pstate;  			break;  		case 17: -			reg = (void *)&task_pt_regs(target)->orig_x0; +			reg = task_pt_regs(target)->orig_x0;  			break;  		default: -			reg = (void *)&task_pt_regs(target)->regs[idx]; +			reg = task_pt_regs(target)->regs[idx];  		} -		ret = copy_to_user(ubuf, reg, sizeof(compat_ulong_t)); +		if (kbuf) { +			memcpy(kbuf, ®, sizeof(reg)); +			kbuf += sizeof(reg); +		} else { +			ret = copy_to_user(ubuf, ®, sizeof(reg)); +			if (ret) +				break; -		if (ret) -			break; -		else -			ubuf += sizeof(compat_ulong_t); +			ubuf += sizeof(reg); +		}  	}  	return ret; @@ -685,28 +692,33 @@ static int compat_gpr_set(struct task_struct *target,  	for (i = 0; i < num_regs; ++i) {  		unsigned int idx = start + i; -		void *reg; +		compat_ulong_t reg; + +		if (kbuf) { +			memcpy(®, kbuf, sizeof(reg)); +			kbuf += sizeof(reg); +		} else { +			ret = copy_from_user(®, ubuf, sizeof(reg)); +			if (ret) +				return ret; + +			ubuf += sizeof(reg); +		}  		switch (idx) {  		case 15: -			reg = (void *)&newregs.pc; +			newregs.pc = reg;  			break;  		case 16: -			reg = (void *)&newregs.pstate; +			newregs.pstate = reg;  			break;  		case 17: -			reg = (void *)&newregs.orig_x0; +			newregs.orig_x0 = reg;  			break;  		default: -			reg = (void *)&newregs.regs[idx]; +			newregs.regs[idx] = reg;  		} -		ret = copy_from_user(reg, ubuf, sizeof(compat_ulong_t)); - -		if (ret) -			goto out; -		else -			ubuf += sizeof(compat_ulong_t);  	}  	if (valid_user_regs(&newregs.user_regs)) @@ -714,7 +726,6 @@ static int compat_gpr_set(struct task_struct *target,  	else  		ret = -EINVAL; -out:  	return ret;  } @@ -768,6 +779,7 @@ static int compat_vfp_set(struct task_struct *target,  		uregs->fpcr = fpscr & VFP_FPSCR_CTRL_MASK;  	} +	fpsimd_flush_task_state(target);  	return ret;  } @@ -825,6 +837,7 @@ static int compat_ptrace_write_user(struct task_struct *tsk, compat_ulong_t off,  				    compat_ulong_t val)  {  	int ret; +	mm_segment_t old_fs = get_fs();  	if (off & 3 || off >= COMPAT_USER_SZ)  		return -EIO; @@ -832,10 +845,13 @@ static int compat_ptrace_write_user(struct task_struct *tsk, compat_ulong_t off,  	if (off >= sizeof(compat_elf_gregset_t))  		return 0; +	set_fs(KERNEL_DS);  	ret = copy_regset_from_user(tsk, &user_aarch32_view,  				    REGSET_COMPAT_GPR, off,  				    sizeof(compat_ulong_t),  				    &val); +	set_fs(old_fs); +  	return ret;  } @@ -1062,35 +1078,49 @@ long arch_ptrace(struct task_struct *child, long request,  	return ptrace_request(child, request, addr, data);  } -asmlinkage int syscall_trace(int dir, struct pt_regs *regs) +enum ptrace_syscall_dir { +	PTRACE_SYSCALL_ENTER = 0, +	PTRACE_SYSCALL_EXIT, +}; + +static void tracehook_report_syscall(struct pt_regs *regs, +				     enum ptrace_syscall_dir dir)  { +	int regno;  	unsigned long saved_reg; -	if (!test_thread_flag(TIF_SYSCALL_TRACE)) -		return regs->syscallno; - -	if (is_compat_task()) { -		/* AArch32 uses ip (r12) for scratch */ -		saved_reg = regs->regs[12]; -		regs->regs[12] = dir; -	} else { -		/* -		 * Save X7. X7 is used to denote syscall entry/exit: -		 *   X7 = 0 -> entry, = 1 -> exit -		 */ -		saved_reg = regs->regs[7]; -		regs->regs[7] = dir; -	} +	/* +	 * A scratch register (ip(r12) on AArch32, x7 on AArch64) is +	 * used to denote syscall entry/exit: +	 */ +	regno = (is_compat_task() ? 12 : 7); +	saved_reg = regs->regs[regno]; +	regs->regs[regno] = dir; -	if (dir) +	if (dir == PTRACE_SYSCALL_EXIT)  		tracehook_report_syscall_exit(regs, 0);  	else if (tracehook_report_syscall_entry(regs))  		regs->syscallno = ~0UL; -	if (is_compat_task()) -		regs->regs[12] = saved_reg; -	else -		regs->regs[7] = saved_reg; +	regs->regs[regno] = saved_reg; +} + +asmlinkage int syscall_trace_enter(struct pt_regs *regs) +{ +	if (test_thread_flag(TIF_SYSCALL_TRACE)) +		tracehook_report_syscall(regs, PTRACE_SYSCALL_ENTER); + +	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) +		trace_sys_enter(regs, regs->syscallno);  	return regs->syscallno;  } + +asmlinkage void syscall_trace_exit(struct pt_regs *regs) +{ +	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) +		trace_sys_exit(regs, regs_return_value(regs)); + +	if (test_thread_flag(TIF_SYSCALL_TRACE)) +		tracehook_report_syscall(regs, PTRACE_SYSCALL_EXIT); +} diff --git a/arch/arm64/kernel/return_address.c b/arch/arm64/kernel/return_address.c new file mode 100644 index 00000000000..89102a6ffad --- /dev/null +++ b/arch/arm64/kernel/return_address.c @@ -0,0 +1,55 @@ +/* + * arch/arm64/kernel/return_address.c + * + * Copyright (C) 2013 Linaro Limited + * Author: AKASHI Takahiro <takahiro.akashi@linaro.org> + * + * 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/export.h> +#include <linux/ftrace.h> + +#include <asm/stacktrace.h> + +struct return_address_data { +	unsigned int level; +	void *addr; +}; + +static int save_return_addr(struct stackframe *frame, void *d) +{ +	struct return_address_data *data = d; + +	if (!data->level) { +		data->addr = (void *)frame->pc; +		return 1; +	} else { +		--data->level; +		return 0; +	} +} + +void *return_address(unsigned int level) +{ +	struct return_address_data data; +	struct stackframe frame; +	register unsigned long current_sp asm ("sp"); + +	data.level = level + 2; +	data.addr = NULL; + +	frame.fp = (unsigned long)__builtin_frame_address(0); +	frame.sp = current_sp; +	frame.pc = (unsigned long)return_address; /* dummy */ + +	walk_stackframe(&frame, save_return_addr, &data); + +	if (!data.level) +		return data.addr; +	else +		return NULL; +} +EXPORT_SYMBOL_GPL(return_address); diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 055cfb80e05..46d1125571f 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -25,6 +25,7 @@  #include <linux/utsname.h>  #include <linux/initrd.h>  #include <linux/console.h> +#include <linux/cache.h>  #include <linux/bootmem.h>  #include <linux/seq_file.h>  #include <linux/screen_info.h> @@ -41,10 +42,13 @@  #include <linux/memblock.h>  #include <linux/of_fdt.h>  #include <linux/of_platform.h> +#include <linux/efi.h> +#include <asm/fixmap.h>  #include <asm/cputype.h>  #include <asm/elf.h>  #include <asm/cputable.h> +#include <asm/cpu_ops.h>  #include <asm/sections.h>  #include <asm/setup.h>  #include <asm/smp_plat.h> @@ -53,6 +57,7 @@  #include <asm/traps.h>  #include <asm/memblock.h>  #include <asm/psci.h> +#include <asm/efi.h>  unsigned int processor_id;  EXPORT_SYMBOL(processor_id); @@ -60,6 +65,17 @@ EXPORT_SYMBOL(processor_id);  unsigned long elf_hwcap __read_mostly;  EXPORT_SYMBOL_GPL(elf_hwcap); +#ifdef CONFIG_COMPAT +#define COMPAT_ELF_HWCAP_DEFAULT	\ +				(COMPAT_HWCAP_HALF|COMPAT_HWCAP_THUMB|\ +				 COMPAT_HWCAP_FAST_MULT|COMPAT_HWCAP_EDSP|\ +				 COMPAT_HWCAP_TLS|COMPAT_HWCAP_VFP|\ +				 COMPAT_HWCAP_VFPv3|COMPAT_HWCAP_VFPv4|\ +				 COMPAT_HWCAP_NEON|COMPAT_HWCAP_IDIV) +unsigned int compat_elf_hwcap __read_mostly = COMPAT_ELF_HWCAP_DEFAULT; +unsigned int compat_elf_hwcap2 __read_mostly; +#endif +  static const char *cpu_name;  static const char *machine_name;  phys_addr_t __fdt_pointer __initdata; @@ -97,15 +113,97 @@ void __init early_print(const char *str, ...)  	printk("%s", buf);  } -static void __init setup_processor(void) +void __init smp_setup_processor_id(void)  { -	struct cpu_info *cpu_info; +	/* +	 * clear __my_cpu_offset on boot CPU to avoid hang caused by +	 * using percpu variable early, for example, lockdep will +	 * access percpu variable inside lock_release +	 */ +	set_my_cpu_offset(0); +} + +bool arch_match_cpu_phys_id(int cpu, u64 phys_id) +{ +	return phys_id == cpu_logical_map(cpu); +} +struct mpidr_hash mpidr_hash; +#ifdef CONFIG_SMP +/** + * smp_build_mpidr_hash - Pre-compute shifts required at each affinity + *			  level in order to build a linear index from an + *			  MPIDR value. Resulting algorithm is a collision + *			  free hash carried out through shifting and ORing + */ +static void __init smp_build_mpidr_hash(void) +{ +	u32 i, affinity, fs[4], bits[4], ls; +	u64 mask = 0; +	/* +	 * Pre-scan the list of MPIDRS and filter out bits that do +	 * not contribute to affinity levels, ie they never toggle. +	 */ +	for_each_possible_cpu(i) +		mask |= (cpu_logical_map(i) ^ cpu_logical_map(0)); +	pr_debug("mask of set bits %#llx\n", mask);  	/* -	 * locate processor in the list of supported processor -	 * types.  The linker builds this table for us from the -	 * entries in arch/arm/mm/proc.S +	 * Find and stash the last and first bit set at all affinity levels to +	 * check how many bits are required to represent them.  	 */ +	for (i = 0; i < 4; i++) { +		affinity = MPIDR_AFFINITY_LEVEL(mask, i); +		/* +		 * Find the MSB bit and LSB bits position +		 * to determine how many bits are required +		 * to express the affinity level. +		 */ +		ls = fls(affinity); +		fs[i] = affinity ? ffs(affinity) - 1 : 0; +		bits[i] = ls - fs[i]; +	} +	/* +	 * An index can be created from the MPIDR_EL1 by isolating the +	 * significant bits at each affinity level and by shifting +	 * them in order to compress the 32 bits values space to a +	 * compressed set of values. This is equivalent to hashing +	 * the MPIDR_EL1 through shifting and ORing. It is a collision free +	 * hash though not minimal since some levels might contain a number +	 * of CPUs that is not an exact power of 2 and their bit +	 * representation might contain holes, eg MPIDR_EL1[7:0] = {0x2, 0x80}. +	 */ +	mpidr_hash.shift_aff[0] = MPIDR_LEVEL_SHIFT(0) + fs[0]; +	mpidr_hash.shift_aff[1] = MPIDR_LEVEL_SHIFT(1) + fs[1] - bits[0]; +	mpidr_hash.shift_aff[2] = MPIDR_LEVEL_SHIFT(2) + fs[2] - +						(bits[1] + bits[0]); +	mpidr_hash.shift_aff[3] = MPIDR_LEVEL_SHIFT(3) + +				  fs[3] - (bits[2] + bits[1] + bits[0]); +	mpidr_hash.mask = mask; +	mpidr_hash.bits = bits[3] + bits[2] + bits[1] + bits[0]; +	pr_debug("MPIDR hash: aff0[%u] aff1[%u] aff2[%u] aff3[%u] mask[%#llx] bits[%u]\n", +		mpidr_hash.shift_aff[0], +		mpidr_hash.shift_aff[1], +		mpidr_hash.shift_aff[2], +		mpidr_hash.shift_aff[3], +		mpidr_hash.mask, +		mpidr_hash.bits); +	/* +	 * 4x is an arbitrary value used to warn on a hash table much bigger +	 * than expected on most systems. +	 */ +	if (mpidr_hash_size() > 4 * num_possible_cpus()) +		pr_warn("Large number of MPIDR hash buckets detected\n"); +	__flush_dcache_area(&mpidr_hash, sizeof(struct mpidr_hash)); +} +#endif + +static void __init setup_processor(void) +{ +	struct cpu_info *cpu_info; +	u64 features, block; +	u32 cwg; +	int cls; +  	cpu_info = lookup_processor_type(read_cpuid_id());  	if (!cpu_info) {  		printk("CPU configuration botched (ID %08x), unable to continue.\n", @@ -118,76 +216,99 @@ static void __init setup_processor(void)  	printk("CPU: %s [%08x] revision %d\n",  	       cpu_name, read_cpuid_id(), read_cpuid_id() & 15); -	sprintf(init_utsname()->machine, "aarch64"); +	sprintf(init_utsname()->machine, ELF_PLATFORM);  	elf_hwcap = 0; -} -static void __init setup_machine_fdt(phys_addr_t dt_phys) -{ -	struct boot_param_header *devtree; -	unsigned long dt_root; +	/* +	 * Check for sane CTR_EL0.CWG value. +	 */ +	cwg = cache_type_cwg(); +	cls = cache_line_size(); +	if (!cwg) +		pr_warn("No Cache Writeback Granule information, assuming cache line size %d\n", +			cls); +	if (L1_CACHE_BYTES < cls) +		pr_warn("L1_CACHE_BYTES smaller than the Cache Writeback Granule (%d < %d)\n", +			L1_CACHE_BYTES, cls); -	/* Check we have a non-NULL DT pointer */ -	if (!dt_phys) { -		early_print("\n" -			"Error: NULL or invalid device tree blob\n" -			"The dtb must be 8-byte aligned and passed in the first 512MB of memory\n" -			"\nPlease check your bootloader.\n"); +	/* +	 * ID_AA64ISAR0_EL1 contains 4-bit wide signed feature blocks. +	 * The blocks we test below represent incremental functionality +	 * for non-negative values. Negative values are reserved. +	 */ +	features = read_cpuid(ID_AA64ISAR0_EL1); +	block = (features >> 4) & 0xf; +	if (!(block & 0x8)) { +		switch (block) { +		default: +		case 2: +			elf_hwcap |= HWCAP_PMULL; +		case 1: +			elf_hwcap |= HWCAP_AES; +		case 0: +			break; +		} +	} -		while (true) -			cpu_relax(); +	block = (features >> 8) & 0xf; +	if (block && !(block & 0x8)) +		elf_hwcap |= HWCAP_SHA1; + +	block = (features >> 12) & 0xf; +	if (block && !(block & 0x8)) +		elf_hwcap |= HWCAP_SHA2; + +	block = (features >> 16) & 0xf; +	if (block && !(block & 0x8)) +		elf_hwcap |= HWCAP_CRC32; +#ifdef CONFIG_COMPAT +	/* +	 * ID_ISAR5_EL1 carries similar information as above, but pertaining to +	 * the Aarch32 32-bit execution state. +	 */ +	features = read_cpuid(ID_ISAR5_EL1); +	block = (features >> 4) & 0xf; +	if (!(block & 0x8)) { +		switch (block) { +		default: +		case 2: +			compat_elf_hwcap2 |= COMPAT_HWCAP2_PMULL; +		case 1: +			compat_elf_hwcap2 |= COMPAT_HWCAP2_AES; +		case 0: +			break; +		}  	} -	devtree = phys_to_virt(dt_phys); +	block = (features >> 8) & 0xf; +	if (block && !(block & 0x8)) +		compat_elf_hwcap2 |= COMPAT_HWCAP2_SHA1; -	/* Check device tree validity */ -	if (be32_to_cpu(devtree->magic) != OF_DT_HEADER) { +	block = (features >> 12) & 0xf; +	if (block && !(block & 0x8)) +		compat_elf_hwcap2 |= COMPAT_HWCAP2_SHA2; + +	block = (features >> 16) & 0xf; +	if (block && !(block & 0x8)) +		compat_elf_hwcap2 |= COMPAT_HWCAP2_CRC32; +#endif +} + +static void __init setup_machine_fdt(phys_addr_t dt_phys) +{ +	if (!dt_phys || !early_init_dt_scan(phys_to_virt(dt_phys))) {  		early_print("\n"  			"Error: invalid device tree blob at physical address 0x%p (virtual address 0x%p)\n" -			"Expected 0x%x, found 0x%x\n" +			"The dtb must be 8-byte aligned and passed in the first 512MB of memory\n"  			"\nPlease check your bootloader.\n", -			dt_phys, devtree, OF_DT_HEADER, -			be32_to_cpu(devtree->magic)); +			dt_phys, phys_to_virt(dt_phys));  		while (true)  			cpu_relax();  	} -	initial_boot_params = devtree; -	dt_root = of_get_flat_dt_root(); - -	machine_name = of_get_flat_dt_prop(dt_root, "model", NULL); -	if (!machine_name) -		machine_name = of_get_flat_dt_prop(dt_root, "compatible", NULL); -	if (!machine_name) -		machine_name = "<unknown>"; -	pr_info("Machine: %s\n", machine_name); - -	/* Retrieve various information from the /chosen node */ -	of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line); -	/* Initialize {size,address}-cells info */ -	of_scan_flat_dt(early_init_dt_scan_root, NULL); -	/* Setup memory, calling early_init_dt_add_memory_arch */ -	of_scan_flat_dt(early_init_dt_scan_memory, NULL); -} - -void __init early_init_dt_add_memory_arch(u64 base, u64 size) -{ -	base &= PAGE_MASK; -	size &= PAGE_MASK; -	if (base + size < PHYS_OFFSET) { -		pr_warning("Ignoring memory block 0x%llx - 0x%llx\n", -			   base, base + size); -		return; -	} -	if (base < PHYS_OFFSET) { -		pr_warning("Ignoring memory range 0x%llx - 0x%llx\n", -			   base, PHYS_OFFSET); -		size -= PHYS_OFFSET - base; -		base = PHYS_OFFSET; -	} -	memblock_add(base, size); +	machine_name = of_flat_dt_get_machine_name();  }  /* @@ -241,6 +362,11 @@ u64 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID };  void __init setup_arch(char **cmdline_p)  { +	/* +	 * Unmask asynchronous aborts early to catch possible system errors. +	 */ +	local_async_enable(); +  	setup_processor();  	setup_machine_fdt(__fdt_pointer); @@ -252,20 +378,27 @@ void __init setup_arch(char **cmdline_p)  	*cmdline_p = boot_command_line; +	early_ioremap_init(); +  	parse_early_param(); +	efi_init();  	arm64_memblock_init();  	paging_init();  	request_standard_resources(); +	efi_idmap_init(); +  	unflatten_device_tree();  	psci_init();  	cpu_logical_map(0) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK; +	cpu_read_bootcpu_ops();  #ifdef CONFIG_SMP  	smp_init_cpus(); +	smp_build_mpidr_hash();  #endif  #ifdef CONFIG_VT @@ -279,11 +412,10 @@ void __init setup_arch(char **cmdline_p)  static int __init arm64_device_init(void)  { -	of_clk_init(NULL);  	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);  	return 0;  } -arch_initcall(arm64_device_init); +arch_initcall_sync(arm64_device_init);  static DEFINE_PER_CPU(struct cpu, cpu_data); @@ -304,6 +436,12 @@ subsys_initcall(topology_init);  static const char *hwcap_str[] = {  	"fp",  	"asimd", +	"evtstrm", +	"aes", +	"pmull", +	"sha1", +	"sha2", +	"crc32",  	NULL  }; diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c index 890a591f75d..6357b9c6c90 100644 --- a/arch/arm64/kernel/signal.c +++ b/arch/arm64/kernel/signal.c @@ -17,6 +17,7 @@   * along with this program.  If not, see <http://www.gnu.org/licenses/>.   */ +#include <linux/compat.h>  #include <linux/errno.h>  #include <linux/signal.h>  #include <linux/personality.h> @@ -25,7 +26,6 @@  #include <linux/tracehook.h>  #include <linux/ratelimit.h> -#include <asm/compat.h>  #include <asm/debug-monitors.h>  #include <asm/elf.h>  #include <asm/cacheflush.h> @@ -51,7 +51,7 @@ static int preserve_fpsimd_context(struct fpsimd_context __user *ctx)  	int err;  	/* dump the hardware registers to the fpsimd_state structure */ -	fpsimd_save_state(fpsimd); +	fpsimd_preserve_current_state();  	/* copy the FP and status/control registers */  	err = __copy_to_user(ctx->vregs, fpsimd->vregs, sizeof(fpsimd->vregs)); @@ -86,11 +86,8 @@ static int restore_fpsimd_context(struct fpsimd_context __user *ctx)  	__get_user_error(fpsimd.fpcr, &ctx->fpcr, err);  	/* load the hardware registers from the fpsimd_state structure */ -	if (!err) { -		preempt_disable(); -		fpsimd_load_state(&fpsimd); -		preempt_enable(); -	} +	if (!err) +		fpsimd_update_current_state(&fpsimd);  	return err ? -EFAULT : 0;  } @@ -100,8 +97,7 @@ static int restore_sigframe(struct pt_regs *regs,  {  	sigset_t set;  	int i, err; -	struct aux_context __user *aux = -		(struct aux_context __user *)sf->uc.uc_mcontext.__reserved; +	void *aux = sf->uc.uc_mcontext.__reserved;  	err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set));  	if (err == 0) @@ -121,8 +117,11 @@ static int restore_sigframe(struct pt_regs *regs,  	err |= !valid_user_regs(®s->user_regs); -	if (err == 0) -		err |= restore_fpsimd_context(&aux->fpsimd); +	if (err == 0) { +		struct fpsimd_context *fpsimd_ctx = +			container_of(aux, struct fpsimd_context, head); +		err |= restore_fpsimd_context(fpsimd_ctx); +	}  	return err;  } @@ -167,8 +166,8 @@ static int setup_sigframe(struct rt_sigframe __user *sf,  			  struct pt_regs *regs, sigset_t *set)  {  	int i, err = 0; -	struct aux_context __user *aux = -		(struct aux_context __user *)sf->uc.uc_mcontext.__reserved; +	void *aux = sf->uc.uc_mcontext.__reserved; +	struct _aarch64_ctx *end;  	/* set up the stack frame for unwinding */  	__put_user_error(regs->regs[29], &sf->fp, err); @@ -185,12 +184,27 @@ static int setup_sigframe(struct rt_sigframe __user *sf,  	err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(*set)); -	if (err == 0) -		err |= preserve_fpsimd_context(&aux->fpsimd); +	if (err == 0) { +		struct fpsimd_context *fpsimd_ctx = +			container_of(aux, struct fpsimd_context, head); +		err |= preserve_fpsimd_context(fpsimd_ctx); +		aux += sizeof(*fpsimd_ctx); +	} + +	/* fault information, if valid */ +	if (current->thread.fault_code) { +		struct esr_context *esr_ctx = +			container_of(aux, struct esr_context, head); +		__put_user_error(ESR_MAGIC, &esr_ctx->head.magic, err); +		__put_user_error(sizeof(*esr_ctx), &esr_ctx->head.size, err); +		__put_user_error(current->thread.fault_code, &esr_ctx->esr, err); +		aux += sizeof(*esr_ctx); +	}  	/* set the "end" magic */ -	__put_user_error(0, &aux->end.magic, err); -	__put_user_error(0, &aux->end.size, err); +	end = aux; +	__put_user_error(0, &end->magic, err); +	__put_user_error(0, &end->size, err);  	return err;  } @@ -416,4 +430,8 @@ asmlinkage void do_notify_resume(struct pt_regs *regs,  		clear_thread_flag(TIF_NOTIFY_RESUME);  		tracehook_notify_resume(regs);  	} + +	if (thread_flags & _TIF_FOREIGN_FPSTATE) +		fpsimd_restore_current_state(); +  } diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c index e393174fe85..3491c638f17 100644 --- a/arch/arm64/kernel/signal32.c +++ b/arch/arm64/kernel/signal32.c @@ -23,6 +23,7 @@  #include <linux/syscalls.h>  #include <linux/ratelimit.h> +#include <asm/esr.h>  #include <asm/fpsimd.h>  #include <asm/signal32.h>  #include <asm/uaccess.h> @@ -81,6 +82,8 @@ struct compat_vfp_sigframe {  #define VFP_MAGIC		0x56465001  #define VFP_STORAGE_SIZE	sizeof(struct compat_vfp_sigframe) +#define FSR_WRITE_SHIFT		(11) +  struct compat_aux_sigframe {  	struct compat_vfp_sigframe	vfp; @@ -100,34 +103,6 @@ struct compat_rt_sigframe {  #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) -/* - * For ARM syscalls, the syscall number has to be loaded into r7. - * We do not support an OABI userspace. - */ -#define MOV_R7_NR_SIGRETURN	(0xe3a07000 | __NR_compat_sigreturn) -#define SVC_SYS_SIGRETURN	(0xef000000 | __NR_compat_sigreturn) -#define MOV_R7_NR_RT_SIGRETURN	(0xe3a07000 | __NR_compat_rt_sigreturn) -#define SVC_SYS_RT_SIGRETURN	(0xef000000 | __NR_compat_rt_sigreturn) - -/* - * For Thumb syscalls, we also pass the syscall number via r7. We therefore - * need two 16-bit instructions. - */ -#define SVC_THUMB_SIGRETURN	(((0xdf00 | __NR_compat_sigreturn) << 16) | \ -				   0x2700 | __NR_compat_sigreturn) -#define SVC_THUMB_RT_SIGRETURN	(((0xdf00 | __NR_compat_rt_sigreturn) << 16) | \ -				   0x2700 | __NR_compat_rt_sigreturn) - -const compat_ulong_t aarch32_sigret_code[6] = { -	/* -	 * AArch32 sigreturn code. -	 * We don't construct an OABI SWI - instead we just set the imm24 field -	 * to the EABI syscall number so that we create a sane disassembly. -	 */ -	MOV_R7_NR_SIGRETURN,    SVC_SYS_SIGRETURN,    SVC_THUMB_SIGRETURN, -	MOV_R7_NR_RT_SIGRETURN, SVC_SYS_RT_SIGRETURN, SVC_THUMB_RT_SIGRETURN, -}; -  static inline int put_sigset_t(compat_sigset_t __user *uset, sigset_t *set)  {  	compat_sigset_t	cset; @@ -150,7 +125,7 @@ static inline int get_sigset_t(sigset_t *set,  	return 0;  } -int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from) +int copy_siginfo_to_user32(compat_siginfo_t __user *to, const siginfo_t *from)  {  	int err; @@ -247,7 +222,7 @@ static int compat_preserve_vfp_context(struct compat_vfp_sigframe __user *frame)  	 * Note that this also saves V16-31, which aren't visible  	 * in AArch32.  	 */ -	fpsimd_save_state(fpsimd); +	fpsimd_preserve_current_state();  	/* Place structure header on the stack */  	__put_user_error(magic, &frame->magic, err); @@ -310,11 +285,8 @@ static int compat_restore_vfp_context(struct compat_vfp_sigframe __user *frame)  	 * We don't need to touch the exception register, so  	 * reload the hardware state.  	 */ -	if (!err) { -		preempt_disable(); -		fpsimd_load_state(&fpsimd); -		preempt_enable(); -	} +	if (!err) +		fpsimd_update_current_state(&fpsimd);  	return err ? -EFAULT : 0;  } @@ -474,12 +446,13 @@ static void compat_setup_return(struct pt_regs *regs, struct k_sigaction *ka,  	/* Check if the handler is written for ARM or Thumb */  	thumb = handler & 1; -	if (thumb) { +	if (thumb)  		spsr |= COMPAT_PSR_T_BIT; -		spsr &= ~COMPAT_PSR_IT_MASK; -	} else { +	else  		spsr &= ~COMPAT_PSR_T_BIT; -	} + +	/* The IT state must be cleared for both ARM and Thumb-2 */ +	spsr &= ~COMPAT_PSR_IT_MASK;  	if (ka->sa.sa_flags & SA_RESTORER) {  		retcode = ptr_to_compat(ka->sa.sa_restorer); @@ -527,7 +500,9 @@ static int compat_setup_sigframe(struct compat_sigframe __user *sf,  	__put_user_error(regs->pstate, &sf->uc.uc_mcontext.arm_cpsr, err);  	__put_user_error((compat_ulong_t)0, &sf->uc.uc_mcontext.trap_no, err); -	__put_user_error((compat_ulong_t)0, &sf->uc.uc_mcontext.error_code, err); +	/* set the compat FSR WnR */ +	__put_user_error(!!(current->thread.fault_code & ESR_EL1_WRITE) << +			 FSR_WRITE_SHIFT, &sf->uc.uc_mcontext.error_code, err);  	__put_user_error(current->thread.fault_address, &sf->uc.uc_mcontext.fault_address, err);  	__put_user_error(set->sig[0], &sf->uc.uc_mcontext.oldmask, err); diff --git a/arch/arm64/kernel/sleep.S b/arch/arm64/kernel/sleep.S new file mode 100644 index 00000000000..b1925729c69 --- /dev/null +++ b/arch/arm64/kernel/sleep.S @@ -0,0 +1,184 @@ +#include <linux/errno.h> +#include <linux/linkage.h> +#include <asm/asm-offsets.h> +#include <asm/assembler.h> + +	.text +/* + * Implementation of MPIDR_EL1 hash algorithm through shifting + * and OR'ing. + * + * @dst: register containing hash result + * @rs0: register containing affinity level 0 bit shift + * @rs1: register containing affinity level 1 bit shift + * @rs2: register containing affinity level 2 bit shift + * @rs3: register containing affinity level 3 bit shift + * @mpidr: register containing MPIDR_EL1 value + * @mask: register containing MPIDR mask + * + * Pseudo C-code: + * + *u32 dst; + * + *compute_mpidr_hash(u32 rs0, u32 rs1, u32 rs2, u32 rs3, u64 mpidr, u64 mask) { + *	u32 aff0, aff1, aff2, aff3; + *	u64 mpidr_masked = mpidr & mask; + *	aff0 = mpidr_masked & 0xff; + *	aff1 = mpidr_masked & 0xff00; + *	aff2 = mpidr_masked & 0xff0000; + *	aff2 = mpidr_masked & 0xff00000000; + *	dst = (aff0 >> rs0 | aff1 >> rs1 | aff2 >> rs2 | aff3 >> rs3); + *} + * Input registers: rs0, rs1, rs2, rs3, mpidr, mask + * Output register: dst + * Note: input and output registers must be disjoint register sets +         (eg: a macro instance with mpidr = x1 and dst = x1 is invalid) + */ +	.macro compute_mpidr_hash dst, rs0, rs1, rs2, rs3, mpidr, mask +	and	\mpidr, \mpidr, \mask		// mask out MPIDR bits +	and	\dst, \mpidr, #0xff		// mask=aff0 +	lsr	\dst ,\dst, \rs0		// dst=aff0>>rs0 +	and	\mask, \mpidr, #0xff00		// mask = aff1 +	lsr	\mask ,\mask, \rs1 +	orr	\dst, \dst, \mask		// dst|=(aff1>>rs1) +	and	\mask, \mpidr, #0xff0000	// mask = aff2 +	lsr	\mask ,\mask, \rs2 +	orr	\dst, \dst, \mask		// dst|=(aff2>>rs2) +	and	\mask, \mpidr, #0xff00000000	// mask = aff3 +	lsr	\mask ,\mask, \rs3 +	orr	\dst, \dst, \mask		// dst|=(aff3>>rs3) +	.endm +/* + * Save CPU state for a suspend.  This saves callee registers, and allocates + * space on the kernel stack to save the CPU specific registers + some + * other data for resume. + * + *  x0 = suspend finisher argument + */ +ENTRY(__cpu_suspend) +	stp	x29, lr, [sp, #-96]! +	stp	x19, x20, [sp,#16] +	stp	x21, x22, [sp,#32] +	stp	x23, x24, [sp,#48] +	stp	x25, x26, [sp,#64] +	stp	x27, x28, [sp,#80] +	mov	x2, sp +	sub	sp, sp, #CPU_SUSPEND_SZ	// allocate cpu_suspend_ctx +	mov	x1, sp +	/* +	 * x1 now points to struct cpu_suspend_ctx allocated on the stack +	 */ +	str	x2, [x1, #CPU_CTX_SP] +	ldr	x2, =sleep_save_sp +	ldr	x2, [x2, #SLEEP_SAVE_SP_VIRT] +#ifdef CONFIG_SMP +	mrs	x7, mpidr_el1 +	ldr	x9, =mpidr_hash +	ldr	x10, [x9, #MPIDR_HASH_MASK] +	/* +	 * Following code relies on the struct mpidr_hash +	 * members size. +	 */ +	ldp	w3, w4, [x9, #MPIDR_HASH_SHIFTS] +	ldp	w5, w6, [x9, #(MPIDR_HASH_SHIFTS + 8)] +	compute_mpidr_hash x8, x3, x4, x5, x6, x7, x10 +	add	x2, x2, x8, lsl #3 +#endif +	bl	__cpu_suspend_finisher +        /* +	 * Never gets here, unless suspend fails. +	 * Successful cpu_suspend should return from cpu_resume, returning +	 * through this code path is considered an error +	 * If the return value is set to 0 force x0 = -EOPNOTSUPP +	 * to make sure a proper error condition is propagated +	 */ +	cmp	x0, #0 +	mov	x3, #-EOPNOTSUPP +	csel	x0, x3, x0, eq +	add	sp, sp, #CPU_SUSPEND_SZ	// rewind stack pointer +	ldp	x19, x20, [sp, #16] +	ldp	x21, x22, [sp, #32] +	ldp	x23, x24, [sp, #48] +	ldp	x25, x26, [sp, #64] +	ldp	x27, x28, [sp, #80] +	ldp	x29, lr, [sp], #96 +	ret +ENDPROC(__cpu_suspend) +	.ltorg + +/* + * x0 must contain the sctlr value retrieved from restored context + */ +ENTRY(cpu_resume_mmu) +	ldr	x3, =cpu_resume_after_mmu +	msr	sctlr_el1, x0		// restore sctlr_el1 +	isb +	br	x3			// global jump to virtual address +ENDPROC(cpu_resume_mmu) +cpu_resume_after_mmu: +	mov	x0, #0			// return zero on success +	ldp	x19, x20, [sp, #16] +	ldp	x21, x22, [sp, #32] +	ldp	x23, x24, [sp, #48] +	ldp	x25, x26, [sp, #64] +	ldp	x27, x28, [sp, #80] +	ldp	x29, lr, [sp], #96 +	ret +ENDPROC(cpu_resume_after_mmu) + +	.data +ENTRY(cpu_resume) +	bl	el2_setup		// if in EL2 drop to EL1 cleanly +#ifdef CONFIG_SMP +	mrs	x1, mpidr_el1 +	adr	x4, mpidr_hash_ptr +	ldr	x5, [x4] +	add	x8, x4, x5		// x8 = struct mpidr_hash phys address +        /* retrieve mpidr_hash members to compute the hash */ +	ldr	x2, [x8, #MPIDR_HASH_MASK] +	ldp	w3, w4, [x8, #MPIDR_HASH_SHIFTS] +	ldp	w5, w6, [x8, #(MPIDR_HASH_SHIFTS + 8)] +	compute_mpidr_hash x7, x3, x4, x5, x6, x1, x2 +        /* x7 contains hash index, let's use it to grab context pointer */ +#else +	mov	x7, xzr +#endif +	adr	x0, sleep_save_sp +	ldr	x0, [x0, #SLEEP_SAVE_SP_PHYS] +	ldr	x0, [x0, x7, lsl #3] +	/* load sp from context */ +	ldr	x2, [x0, #CPU_CTX_SP] +	adr	x1, sleep_idmap_phys +	/* load physical address of identity map page table in x1 */ +	ldr	x1, [x1] +	mov	sp, x2 +	/* +	 * cpu_do_resume expects x0 to contain context physical address +	 * pointer and x1 to contain physical address of 1:1 page tables +	 */ +	bl	cpu_do_resume		// PC relative jump, MMU off +	b	cpu_resume_mmu		// Resume MMU, never returns +ENDPROC(cpu_resume) + +	.align 3 +mpidr_hash_ptr: +	/* +	 * offset of mpidr_hash symbol from current location +	 * used to obtain run-time mpidr_hash address with MMU off +         */ +	.quad	mpidr_hash - . +/* + * physical address of identity mapped page tables + */ +	.type	sleep_idmap_phys, #object +ENTRY(sleep_idmap_phys) +	.quad	0 +/* + * struct sleep_save_sp { + *	phys_addr_t *save_ptr_stash; + *	phys_addr_t save_ptr_stash_phys; + * }; + */ +	.type	sleep_save_sp, #object +ENTRY(sleep_save_sp) +	.space	SLEEP_SAVE_SP_SZ	// struct sleep_save_sp diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 78db90dcc91..40f38f46c8e 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -35,10 +35,12 @@  #include <linux/clockchips.h>  #include <linux/completion.h>  #include <linux/of.h> +#include <linux/irq_work.h>  #include <asm/atomic.h>  #include <asm/cacheflush.h>  #include <asm/cputype.h> +#include <asm/cpu_ops.h>  #include <asm/mmu_context.h>  #include <asm/pgtable.h>  #include <asm/pgalloc.h> @@ -54,70 +56,26 @@   * where to place its SVC stack   */  struct secondary_data secondary_data; -volatile unsigned long secondary_holding_pen_release = INVALID_HWID;  enum ipi_msg_type {  	IPI_RESCHEDULE,  	IPI_CALL_FUNC,  	IPI_CALL_FUNC_SINGLE,  	IPI_CPU_STOP, +	IPI_TIMER, +	IPI_IRQ_WORK,  }; -static DEFINE_RAW_SPINLOCK(boot_lock); - -/* - * Write secondary_holding_pen_release in a way that is guaranteed to be - * visible to all observers, irrespective of whether they're taking part - * in coherency or not.  This is necessary for the hotplug code to work - * reliably. - */ -static void write_pen_release(u64 val) -{ -	void *start = (void *)&secondary_holding_pen_release; -	unsigned long size = sizeof(secondary_holding_pen_release); - -	secondary_holding_pen_release = val; -	__flush_dcache_area(start, size); -} -  /*   * Boot a secondary CPU, and assign it the specified idle task.   * This also gives us the initial stack to use for this CPU.   */  static int boot_secondary(unsigned int cpu, struct task_struct *idle)  { -	unsigned long timeout; - -	/* -	 * Set synchronisation state between this boot processor -	 * and the secondary one -	 */ -	raw_spin_lock(&boot_lock); - -	/* -	 * Update the pen release flag. -	 */ -	write_pen_release(cpu_logical_map(cpu)); - -	/* -	 * Send an event, causing the secondaries to read pen_release. -	 */ -	sev(); - -	timeout = jiffies + (1 * HZ); -	while (time_before(jiffies, timeout)) { -		if (secondary_holding_pen_release == INVALID_HWID) -			break; -		udelay(10); -	} +	if (cpu_ops[cpu]->cpu_boot) +		return cpu_ops[cpu]->cpu_boot(cpu); -	/* -	 * Now the secondary core is starting up let it run its -	 * calibrations, then wait for it to finish -	 */ -	raw_spin_unlock(&boot_lock); - -	return secondary_holding_pen_release != INVALID_HWID ? -ENOSYS : 0; +	return -EOPNOTSUPP;  }  static DECLARE_COMPLETION(cpu_running); @@ -158,6 +116,11 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle)  	return ret;  } +static void smp_store_cpu_info(unsigned int cpuid) +{ +	store_cpu_topology(cpuid); +} +  /*   * This is the secondary CPU boot entry.  We're using this CPUs   * idle thread stack, but a set of temporary page tables. @@ -167,8 +130,6 @@ asmlinkage void secondary_start_kernel(void)  	struct mm_struct *mm = &init_mm;  	unsigned int cpu = smp_processor_id(); -	printk("CPU%u: Booted secondary processor\n", cpu); -  	/*  	 * All kernel threads share the same mm context; grab a  	 * reference and switch to it. @@ -177,6 +138,9 @@ asmlinkage void secondary_start_kernel(void)  	current->active_mm = mm;  	cpumask_set_cpu(cpu, mm_cpumask(mm)); +	set_my_cpu_offset(per_cpu_offset(smp_processor_id())); +	printk("CPU%u: Booted secondary processor\n", cpu); +  	/*  	 * TTBR0 is only used for the identity mapping at this stage. Make it  	 * point to zero page to avoid speculatively fetching new entries. @@ -187,17 +151,15 @@ asmlinkage void secondary_start_kernel(void)  	preempt_disable();  	trace_hardirqs_off(); -	/* -	 * Let the primary processor know we're out of the -	 * pen, then head off into the C entry point -	 */ -	write_pen_release(INVALID_HWID); +	if (cpu_ops[cpu]->cpu_postboot) +		cpu_ops[cpu]->cpu_postboot();  	/* -	 * Synchronise with the boot thread. +	 * Enable GIC and timers.  	 */ -	raw_spin_lock(&boot_lock); -	raw_spin_unlock(&boot_lock); +	notify_cpu_starting(cpu); + +	smp_store_cpu_info(cpu);  	/*  	 * OK, now it's safe to let the boot CPU continue.  Wait for @@ -207,13 +169,9 @@ asmlinkage void secondary_start_kernel(void)  	set_cpu_online(cpu, true);  	complete(&cpu_running); -	/* -	 * Enable GIC and timers. -	 */ -	notify_cpu_starting(cpu); - +	local_dbg_enable();  	local_irq_enable(); -	local_fiq_enable(); +	local_async_enable();  	/*  	 * OK, it's off to the idle thread for us @@ -221,39 +179,136 @@ asmlinkage void secondary_start_kernel(void)  	cpu_startup_entry(CPUHP_ONLINE);  } -void __init smp_cpus_done(unsigned int max_cpus) +#ifdef CONFIG_HOTPLUG_CPU +static int op_cpu_disable(unsigned int cpu)  { -	pr_info("SMP: Total of %d processors activated.\n", num_online_cpus()); +	/* +	 * If we don't have a cpu_die method, abort before we reach the point +	 * of no return. CPU0 may not have an cpu_ops, so test for it. +	 */ +	if (!cpu_ops[cpu] || !cpu_ops[cpu]->cpu_die) +		return -EOPNOTSUPP; + +	/* +	 * We may need to abort a hot unplug for some other mechanism-specific +	 * reason. +	 */ +	if (cpu_ops[cpu]->cpu_disable) +		return cpu_ops[cpu]->cpu_disable(cpu); + +	return 0;  } -void __init smp_prepare_boot_cpu(void) +/* + * __cpu_disable runs on the processor to be shutdown. + */ +int __cpu_disable(void)  { -} +	unsigned int cpu = smp_processor_id(); +	int ret; -static void (*smp_cross_call)(const struct cpumask *, unsigned int); +	ret = op_cpu_disable(cpu); +	if (ret) +		return ret; -static const struct smp_enable_ops *enable_ops[] __initconst = { -	&smp_spin_table_ops, -	&smp_psci_ops, -	NULL, -}; +	/* +	 * Take this CPU offline.  Once we clear this, we can't return, +	 * and we must not schedule until we're ready to give up the cpu. +	 */ +	set_cpu_online(cpu, false); -static const struct smp_enable_ops *smp_enable_ops[NR_CPUS]; +	/* +	 * OK - migrate IRQs away from this CPU +	 */ +	migrate_irqs(); -static const struct smp_enable_ops * __init smp_get_enable_ops(const char *name) +	/* +	 * Remove this CPU from the vm mask set of all processes. +	 */ +	clear_tasks_mm_cpumask(cpu); + +	return 0; +} + +static int op_cpu_kill(unsigned int cpu)  { -	const struct smp_enable_ops **ops = enable_ops; +	/* +	 * If we have no means of synchronising with the dying CPU, then assume +	 * that it is really dead. We can only wait for an arbitrary length of +	 * time and hope that it's dead, so let's skip the wait and just hope. +	 */ +	if (!cpu_ops[cpu]->cpu_kill) +		return 1; + +	return cpu_ops[cpu]->cpu_kill(cpu); +} -	while (*ops) { -		if (!strcmp(name, (*ops)->name)) -			return *ops; +static DECLARE_COMPLETION(cpu_died); -		ops++; +/* + * called on the thread which is asking for a CPU to be shutdown - + * waits until shutdown has completed, or it is timed out. + */ +void __cpu_die(unsigned int cpu) +{ +	if (!wait_for_completion_timeout(&cpu_died, msecs_to_jiffies(5000))) { +		pr_crit("CPU%u: cpu didn't die\n", cpu); +		return;  	} +	pr_notice("CPU%u: shutdown\n", cpu); + +	/* +	 * Now that the dying CPU is beyond the point of no return w.r.t. +	 * in-kernel synchronisation, try to get the firwmare to help us to +	 * verify that it has really left the kernel before we consider +	 * clobbering anything it might still be using. +	 */ +	if (!op_cpu_kill(cpu)) +		pr_warn("CPU%d may not have shut down cleanly\n", cpu); +} + +/* + * Called from the idle thread for the CPU which has been shutdown. + * + * Note that we disable IRQs here, but do not re-enable them + * before returning to the caller. This is also the behaviour + * of the other hotplug-cpu capable cores, so presumably coming + * out of idle fixes this. + */ +void cpu_die(void) +{ +	unsigned int cpu = smp_processor_id(); + +	idle_task_exit(); + +	local_irq_disable(); + +	/* Tell __cpu_die() that this CPU is now safe to dispose of */ +	complete(&cpu_died); + +	/* +	 * Actually shutdown the CPU. This must never fail. The specific hotplug +	 * mechanism must perform all required cache maintenance to ensure that +	 * no dirty lines are lost in the process of shutting down the CPU. +	 */ +	cpu_ops[cpu]->cpu_die(cpu); + +	BUG(); +} +#endif + +void __init smp_cpus_done(unsigned int max_cpus) +{ +	pr_info("SMP: Total of %d processors activated.\n", num_online_cpus()); +} -	return NULL; +void __init smp_prepare_boot_cpu(void) +{ +	set_my_cpu_offset(per_cpu_offset(smp_processor_id()));  } +static void (*smp_cross_call)(const struct cpumask *, unsigned int); +  /*   * Enumerate the possible CPU set from the device tree and build the   * cpu logical map array containing MPIDR values related to logical @@ -261,9 +316,8 @@ static const struct smp_enable_ops * __init smp_get_enable_ops(const char *name)   */  void __init smp_init_cpus(void)  { -	const char *enable_method;  	struct device_node *dn = NULL; -	int i, cpu = 1; +	unsigned int i, cpu = 1;  	bool bootcpu_valid = false;  	while ((dn = of_find_node_by_type(dn, "cpu"))) { @@ -332,25 +386,10 @@ void __init smp_init_cpus(void)  		if (cpu >= NR_CPUS)  			goto next; -		/* -		 * We currently support only the "spin-table" enable-method. -		 */ -		enable_method = of_get_property(dn, "enable-method", NULL); -		if (!enable_method) { -			pr_err("%s: missing enable-method property\n", -				dn->full_name); -			goto next; -		} - -		smp_enable_ops[cpu] = smp_get_enable_ops(enable_method); - -		if (!smp_enable_ops[cpu]) { -			pr_err("%s: invalid enable-method property: %s\n", -			       dn->full_name, enable_method); +		if (cpu_read_ops(dn, cpu) != 0)  			goto next; -		} -		if (smp_enable_ops[cpu]->init_cpu(dn, cpu)) +		if (cpu_ops[cpu]->cpu_init(dn, cpu))  			goto next;  		pr_debug("cpu logical map 0x%llx\n", hwid); @@ -380,8 +419,12 @@ next:  void __init smp_prepare_cpus(unsigned int max_cpus)  { -	int cpu, err; -	unsigned int ncores = num_possible_cpus(); +	int err; +	unsigned int cpu, ncores = num_possible_cpus(); + +	init_cpu_topology(); + +	smp_store_cpu_info(smp_processor_id());  	/*  	 * are we trying to boot more cores than exist? @@ -408,10 +451,10 @@ void __init smp_prepare_cpus(unsigned int max_cpus)  		if (cpu == smp_processor_id())  			continue; -		if (!smp_enable_ops[cpu]) +		if (!cpu_ops[cpu])  			continue; -		err = smp_enable_ops[cpu]->prepare_cpu(cpu); +		err = cpu_ops[cpu]->cpu_prepare(cpu);  		if (err)  			continue; @@ -436,12 +479,22 @@ void arch_send_call_function_single_ipi(int cpu)  	smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE);  } +#ifdef CONFIG_IRQ_WORK +void arch_irq_work_raise(void) +{ +	if (smp_cross_call) +		smp_cross_call(cpumask_of(smp_processor_id()), IPI_IRQ_WORK); +} +#endif +  static const char *ipi_types[NR_IPI] = {  #define S(x,s)	[x - IPI_RESCHEDULE] = s  	S(IPI_RESCHEDULE, "Rescheduling interrupts"),  	S(IPI_CALL_FUNC, "Function call interrupts"),  	S(IPI_CALL_FUNC_SINGLE, "Single function call interrupts"),  	S(IPI_CPU_STOP, "CPU stop interrupts"), +	S(IPI_TIMER, "Timer broadcast interrupts"), +	S(IPI_IRQ_WORK, "IRQ work interrupts"),  };  void show_ipi_list(struct seq_file *p, int prec) @@ -451,7 +504,7 @@ void show_ipi_list(struct seq_file *p, int prec)  	for (i = 0; i < NR_IPI; i++) {  		seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i + IPI_RESCHEDULE,  			   prec >= 4 ? " " : ""); -		for_each_present_cpu(cpu) +		for_each_online_cpu(cpu)  			seq_printf(p, "%10u ",  				   __get_irq_stat(cpu, ipi_irqs[i]));  		seq_printf(p, "      %s\n", ipi_types[i]); @@ -486,7 +539,6 @@ static void ipi_cpu_stop(unsigned int cpu)  	set_cpu_online(cpu, false); -	local_fiq_disable();  	local_irq_disable();  	while (1) @@ -527,6 +579,22 @@ void handle_IPI(int ipinr, struct pt_regs *regs)  		irq_exit();  		break; +#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST +	case IPI_TIMER: +		irq_enter(); +		tick_receive_broadcast(); +		irq_exit(); +		break; +#endif + +#ifdef CONFIG_IRQ_WORK +	case IPI_IRQ_WORK: +		irq_enter(); +		irq_work_run(); +		irq_exit(); +		break; +#endif +  	default:  		pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr);  		break; @@ -539,6 +607,13 @@ void smp_send_reschedule(int cpu)  	smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE);  } +#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST +void tick_broadcast(const struct cpumask *mask) +{ +	smp_cross_call(mask, IPI_TIMER); +} +#endif +  void smp_send_stop(void)  {  	unsigned long timeout; diff --git a/arch/arm64/kernel/smp_psci.c b/arch/arm64/kernel/smp_psci.c deleted file mode 100644 index 0c533301be7..00000000000 --- a/arch/arm64/kernel/smp_psci.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * PSCI SMP initialisation - * - * Copyright (C) 2013 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 <linux/init.h> -#include <linux/of.h> -#include <linux/smp.h> - -#include <asm/psci.h> -#include <asm/smp_plat.h> - -static int __init smp_psci_init_cpu(struct device_node *dn, int cpu) -{ -	return 0; -} - -static int __init smp_psci_prepare_cpu(int cpu) -{ -	int err; - -	if (!psci_ops.cpu_on) { -		pr_err("psci: no cpu_on method, not booting CPU%d\n", cpu); -		return -ENODEV; -	} - -	err = psci_ops.cpu_on(cpu_logical_map(cpu), __pa(secondary_holding_pen)); -	if (err) { -		pr_err("psci: failed to boot CPU%d (%d)\n", cpu, err); -		return err; -	} - -	return 0; -} - -const struct smp_enable_ops smp_psci_ops __initconst = { -	.name		= "psci", -	.init_cpu	= smp_psci_init_cpu, -	.prepare_cpu	= smp_psci_prepare_cpu, -}; diff --git a/arch/arm64/kernel/smp_spin_table.c b/arch/arm64/kernel/smp_spin_table.c index 7c35fa682f7..0347d38eea2 100644 --- a/arch/arm64/kernel/smp_spin_table.c +++ b/arch/arm64/kernel/smp_spin_table.c @@ -16,15 +16,38 @@   * along with this program.  If not, see <http://www.gnu.org/licenses/>.   */ +#include <linux/delay.h>  #include <linux/init.h>  #include <linux/of.h>  #include <linux/smp.h>  #include <asm/cacheflush.h> +#include <asm/cpu_ops.h> +#include <asm/cputype.h> +#include <asm/smp_plat.h> + +extern void secondary_holding_pen(void); +volatile unsigned long secondary_holding_pen_release = INVALID_HWID;  static phys_addr_t cpu_release_addr[NR_CPUS]; -static int __init smp_spin_table_init_cpu(struct device_node *dn, int cpu) +/* + * Write secondary_holding_pen_release in a way that is guaranteed to be + * visible to all observers, irrespective of whether they're taking part + * in coherency or not.  This is necessary for the hotplug code to work + * reliably. + */ +static void write_pen_release(u64 val) +{ +	void *start = (void *)&secondary_holding_pen_release; +	unsigned long size = sizeof(secondary_holding_pen_release); + +	secondary_holding_pen_release = val; +	__flush_dcache_area(start, size); +} + + +static int smp_spin_table_cpu_init(struct device_node *dn, unsigned int cpu)  {  	/*  	 * Determine the address from which the CPU is polling. @@ -40,7 +63,7 @@ static int __init smp_spin_table_init_cpu(struct device_node *dn, int cpu)  	return 0;  } -static int __init smp_spin_table_prepare_cpu(int cpu) +static int smp_spin_table_cpu_prepare(unsigned int cpu)  {  	void **release_addr; @@ -48,7 +71,16 @@ static int __init smp_spin_table_prepare_cpu(int cpu)  		return -ENODEV;  	release_addr = __va(cpu_release_addr[cpu]); -	release_addr[0] = (void *)__pa(secondary_holding_pen); + +	/* +	 * We write the release address as LE regardless of the native +	 * endianess of the kernel. Therefore, any boot-loaders that +	 * read this address need to convert this address to the +	 * boot-loader's endianess before jumping. This is mandated by +	 * the boot protocol. +	 */ +	release_addr[0] = (void *) cpu_to_le64(__pa(secondary_holding_pen)); +  	__flush_dcache_area(release_addr, sizeof(release_addr[0]));  	/* @@ -59,8 +91,24 @@ static int __init smp_spin_table_prepare_cpu(int cpu)  	return 0;  } -const struct smp_enable_ops smp_spin_table_ops __initconst = { +static int smp_spin_table_cpu_boot(unsigned int cpu) +{ +	/* +	 * Update the pen release flag. +	 */ +	write_pen_release(cpu_logical_map(cpu)); + +	/* +	 * Send an event, causing the secondaries to read pen_release. +	 */ +	sev(); + +	return 0; +} + +const struct cpu_operations smp_spin_table_ops = {  	.name		= "spin-table", -	.init_cpu 	= smp_spin_table_init_cpu, -	.prepare_cpu	= smp_spin_table_prepare_cpu, +	.cpu_init	= smp_spin_table_cpu_init, +	.cpu_prepare	= smp_spin_table_cpu_prepare, +	.cpu_boot	= smp_spin_table_cpu_boot,  }; diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index d25459ff57f..55437ba1f5a 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c @@ -35,7 +35,7 @@   *	ldp	x29, x30, [sp]   *	add	sp, sp, #0x10   */ -int unwind_frame(struct stackframe *frame) +int notrace unwind_frame(struct stackframe *frame)  {  	unsigned long high, low;  	unsigned long fp = frame->fp; @@ -43,12 +43,16 @@ int unwind_frame(struct stackframe *frame)  	low  = frame->sp;  	high = ALIGN(low, THREAD_SIZE); -	if (fp < low || fp > high || fp & 0xf) +	if (fp < low || fp > high - 0x18 || fp & 0xf)  		return -EINVAL;  	frame->sp = fp + 0x10;  	frame->fp = *(unsigned long *)(fp); -	frame->pc = *(unsigned long *)(fp + 8); +	/* +	 * -4 here because we care about the PC at time of bl, +	 * not where the return will go. +	 */ +	frame->pc = *(unsigned long *)(fp + 8) - 4;  	return 0;  } diff --git a/arch/arm64/kernel/suspend.c b/arch/arm64/kernel/suspend.c new file mode 100644 index 00000000000..1fa9ce4afd8 --- /dev/null +++ b/arch/arm64/kernel/suspend.c @@ -0,0 +1,140 @@ +#include <linux/percpu.h> +#include <linux/slab.h> +#include <asm/cacheflush.h> +#include <asm/cpu_ops.h> +#include <asm/debug-monitors.h> +#include <asm/pgtable.h> +#include <asm/memory.h> +#include <asm/smp_plat.h> +#include <asm/suspend.h> +#include <asm/tlbflush.h> + +extern int __cpu_suspend(unsigned long); +/* + * This is called by __cpu_suspend() to save the state, and do whatever + * flushing is required to ensure that when the CPU goes to sleep we have + * the necessary data available when the caches are not searched. + * + * @arg: Argument to pass to suspend operations + * @ptr: CPU context virtual address + * @save_ptr: address of the location where the context physical address + *            must be saved + */ +int __cpu_suspend_finisher(unsigned long arg, struct cpu_suspend_ctx *ptr, +			   phys_addr_t *save_ptr) +{ +	int cpu = smp_processor_id(); + +	*save_ptr = virt_to_phys(ptr); + +	cpu_do_suspend(ptr); +	/* +	 * Only flush the context that must be retrieved with the MMU +	 * off. VA primitives ensure the flush is applied to all +	 * cache levels so context is pushed to DRAM. +	 */ +	__flush_dcache_area(ptr, sizeof(*ptr)); +	__flush_dcache_area(save_ptr, sizeof(*save_ptr)); + +	return cpu_ops[cpu]->cpu_suspend(arg); +} + +/* + * This hook is provided so that cpu_suspend code can restore HW + * breakpoints as early as possible in the resume path, before reenabling + * debug exceptions. Code cannot be run from a CPU PM notifier since by the + * time the notifier runs debug exceptions might have been enabled already, + * with HW breakpoints registers content still in an unknown state. + */ +void (*hw_breakpoint_restore)(void *); +void __init cpu_suspend_set_dbg_restorer(void (*hw_bp_restore)(void *)) +{ +	/* Prevent multiple restore hook initializations */ +	if (WARN_ON(hw_breakpoint_restore)) +		return; +	hw_breakpoint_restore = hw_bp_restore; +} + +/** + * cpu_suspend + * + * @arg: argument to pass to the finisher function + */ +int cpu_suspend(unsigned long arg) +{ +	struct mm_struct *mm = current->active_mm; +	int ret, cpu = smp_processor_id(); +	unsigned long flags; + +	/* +	 * If cpu_ops have not been registered or suspend +	 * has not been initialized, cpu_suspend call fails early. +	 */ +	if (!cpu_ops[cpu] || !cpu_ops[cpu]->cpu_suspend) +		return -EOPNOTSUPP; + +	/* +	 * From this point debug exceptions are disabled to prevent +	 * updates to mdscr register (saved and restored along with +	 * general purpose registers) from kernel debuggers. +	 */ +	local_dbg_save(flags); + +	/* +	 * mm context saved on the stack, it will be restored when +	 * the cpu comes out of reset through the identity mapped +	 * page tables, so that the thread address space is properly +	 * set-up on function return. +	 */ +	ret = __cpu_suspend(arg); +	if (ret == 0) { +		cpu_switch_mm(mm->pgd, mm); +		flush_tlb_all(); + +		/* +		 * Restore per-cpu offset before any kernel +		 * subsystem relying on it has a chance to run. +		 */ +		set_my_cpu_offset(per_cpu_offset(cpu)); + +		/* +		 * Restore HW breakpoint registers to sane values +		 * before debug exceptions are possibly reenabled +		 * through local_dbg_restore. +		 */ +		if (hw_breakpoint_restore) +			hw_breakpoint_restore(NULL); +	} + +	/* +	 * Restore pstate flags. OS lock and mdscr have been already +	 * restored, so from this point onwards, debugging is fully +	 * renabled if it was enabled when core started shutdown. +	 */ +	local_dbg_restore(flags); + +	return ret; +} + +extern struct sleep_save_sp sleep_save_sp; +extern phys_addr_t sleep_idmap_phys; + +static int cpu_suspend_init(void) +{ +	void *ctx_ptr; + +	/* ctx_ptr is an array of physical addresses */ +	ctx_ptr = kcalloc(mpidr_hash_size(), sizeof(phys_addr_t), GFP_KERNEL); + +	if (WARN_ON(!ctx_ptr)) +		return -ENOMEM; + +	sleep_save_sp.save_ptr_stash = ctx_ptr; +	sleep_save_sp.save_ptr_stash_phys = virt_to_phys(ctx_ptr); +	sleep_idmap_phys = virt_to_phys(idmap_pg_dir); +	__flush_dcache_area(&sleep_save_sp, sizeof(struct sleep_save_sp)); +	__flush_dcache_area(&sleep_idmap_phys, sizeof(sleep_idmap_phys)); + +	return 0; +} +early_initcall(cpu_suspend_init); diff --git a/arch/arm64/kernel/sys32.S b/arch/arm64/kernel/sys32.S index a1b19ed7467..423a5b3fc2b 100644 --- a/arch/arm64/kernel/sys32.S +++ b/arch/arm64/kernel/sys32.S @@ -59,48 +59,48 @@ ENDPROC(compat_sys_fstatfs64_wrapper)   * extension.   */  compat_sys_pread64_wrapper: -	orr	x3, x4, x5, lsl #32 +	regs_to_64	x3, x4, x5  	b	sys_pread64  ENDPROC(compat_sys_pread64_wrapper)  compat_sys_pwrite64_wrapper: -	orr	x3, x4, x5, lsl #32 +	regs_to_64	x3, x4, x5  	b	sys_pwrite64  ENDPROC(compat_sys_pwrite64_wrapper)  compat_sys_truncate64_wrapper: -	orr	x1, x2, x3, lsl #32 +	regs_to_64	x1, x2, x3  	b	sys_truncate  ENDPROC(compat_sys_truncate64_wrapper)  compat_sys_ftruncate64_wrapper: -	orr	x1, x2, x3, lsl #32 +	regs_to_64	x1, x2, x3  	b	sys_ftruncate  ENDPROC(compat_sys_ftruncate64_wrapper)  compat_sys_readahead_wrapper: -	orr	x1, x2, x3, lsl #32 +	regs_to_64	x1, x2, x3  	mov	w2, w4  	b	sys_readahead  ENDPROC(compat_sys_readahead_wrapper)  compat_sys_fadvise64_64_wrapper:  	mov	w6, w1 -	orr	x1, x2, x3, lsl #32 -	orr	x2, x4, x5, lsl #32 +	regs_to_64	x1, x2, x3 +	regs_to_64	x2, x4, x5  	mov	w3, w6  	b	sys_fadvise64_64  ENDPROC(compat_sys_fadvise64_64_wrapper)  compat_sys_sync_file_range2_wrapper: -	orr	x2, x2, x3, lsl #32 -	orr	x3, x4, x5, lsl #32 +	regs_to_64	x2, x2, x3 +	regs_to_64	x3, x4, x5  	b	sys_sync_file_range2  ENDPROC(compat_sys_sync_file_range2_wrapper)  compat_sys_fallocate_wrapper: -	orr	x2, x2, x3, lsl #32 -	orr	x3, x4, x5, lsl #32 +	regs_to_64	x2, x2, x3 +	regs_to_64	x3, x4, x5  	b	sys_fallocate  ENDPROC(compat_sys_fallocate_wrapper) diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c index 03dc3718eb1..1a7125c3099 100644 --- a/arch/arm64/kernel/time.c +++ b/arch/arm64/kernel/time.c @@ -18,6 +18,7 @@   * along with this program.  If not, see <http://www.gnu.org/licenses/>.   */ +#include <linux/clockchips.h>  #include <linux/export.h>  #include <linux/kernel.h>  #include <linux/interrupt.h> @@ -33,6 +34,7 @@  #include <linux/irq.h>  #include <linux/delay.h>  #include <linux/clocksource.h> +#include <linux/clk-provider.h>  #include <clocksource/arm_arch_timer.h> @@ -61,26 +63,19 @@ unsigned long profile_pc(struct pt_regs *regs)  EXPORT_SYMBOL(profile_pc);  #endif -static u64 sched_clock_mult __read_mostly; - -unsigned long long notrace sched_clock(void) -{ -	return arch_timer_read_counter() * sched_clock_mult; -} -  void __init time_init(void)  {  	u32 arch_timer_rate; +	of_clk_init(NULL);  	clocksource_of_init(); +	tick_setup_hrtimer_broadcast(); +  	arch_timer_rate = arch_timer_get_rate();  	if (!arch_timer_rate)  		panic("Unable to initialise architected timer.\n"); -	/* Cache the sched_clock multiplier to save a divide in the hot path. */ -	sched_clock_mult = NSEC_PER_SEC / arch_timer_rate; -  	/* Calibrate the delay loop directly */  	lpj_fine = arch_timer_rate / HZ;  } diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c new file mode 100644 index 00000000000..43514f90591 --- /dev/null +++ b/arch/arm64/kernel/topology.c @@ -0,0 +1,283 @@ +/* + * arch/arm64/kernel/topology.c + * + * Copyright (C) 2011,2013,2014 Linaro Limited. + * + * Based on the arm32 version written by Vincent Guittot in turn based on + * arch/sh/kernel/topology.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License.  See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/cpu.h> +#include <linux/cpumask.h> +#include <linux/init.h> +#include <linux/percpu.h> +#include <linux/node.h> +#include <linux/nodemask.h> +#include <linux/of.h> +#include <linux/sched.h> + +#include <asm/topology.h> + +static int __init get_cpu_for_node(struct device_node *node) +{ +	struct device_node *cpu_node; +	int cpu; + +	cpu_node = of_parse_phandle(node, "cpu", 0); +	if (!cpu_node) +		return -1; + +	for_each_possible_cpu(cpu) { +		if (of_get_cpu_node(cpu, NULL) == cpu_node) { +			of_node_put(cpu_node); +			return cpu; +		} +	} + +	pr_crit("Unable to find CPU node for %s\n", cpu_node->full_name); + +	of_node_put(cpu_node); +	return -1; +} + +static int __init parse_core(struct device_node *core, int cluster_id, +			     int core_id) +{ +	char name[10]; +	bool leaf = true; +	int i = 0; +	int cpu; +	struct device_node *t; + +	do { +		snprintf(name, sizeof(name), "thread%d", i); +		t = of_get_child_by_name(core, name); +		if (t) { +			leaf = false; +			cpu = get_cpu_for_node(t); +			if (cpu >= 0) { +				cpu_topology[cpu].cluster_id = cluster_id; +				cpu_topology[cpu].core_id = core_id; +				cpu_topology[cpu].thread_id = i; +			} else { +				pr_err("%s: Can't get CPU for thread\n", +				       t->full_name); +				of_node_put(t); +				return -EINVAL; +			} +			of_node_put(t); +		} +		i++; +	} while (t); + +	cpu = get_cpu_for_node(core); +	if (cpu >= 0) { +		if (!leaf) { +			pr_err("%s: Core has both threads and CPU\n", +			       core->full_name); +			return -EINVAL; +		} + +		cpu_topology[cpu].cluster_id = cluster_id; +		cpu_topology[cpu].core_id = core_id; +	} else if (leaf) { +		pr_err("%s: Can't get CPU for leaf core\n", core->full_name); +		return -EINVAL; +	} + +	return 0; +} + +static int __init parse_cluster(struct device_node *cluster, int depth) +{ +	char name[10]; +	bool leaf = true; +	bool has_cores = false; +	struct device_node *c; +	static int cluster_id __initdata; +	int core_id = 0; +	int i, ret; + +	/* +	 * First check for child clusters; we currently ignore any +	 * information about the nesting of clusters and present the +	 * scheduler with a flat list of them. +	 */ +	i = 0; +	do { +		snprintf(name, sizeof(name), "cluster%d", i); +		c = of_get_child_by_name(cluster, name); +		if (c) { +			leaf = false; +			ret = parse_cluster(c, depth + 1); +			of_node_put(c); +			if (ret != 0) +				return ret; +		} +		i++; +	} while (c); + +	/* Now check for cores */ +	i = 0; +	do { +		snprintf(name, sizeof(name), "core%d", i); +		c = of_get_child_by_name(cluster, name); +		if (c) { +			has_cores = true; + +			if (depth == 0) { +				pr_err("%s: cpu-map children should be clusters\n", +				       c->full_name); +				of_node_put(c); +				return -EINVAL; +			} + +			if (leaf) { +				ret = parse_core(c, cluster_id, core_id++); +			} else { +				pr_err("%s: Non-leaf cluster with core %s\n", +				       cluster->full_name, name); +				ret = -EINVAL; +			} + +			of_node_put(c); +			if (ret != 0) +				return ret; +		} +		i++; +	} while (c); + +	if (leaf && !has_cores) +		pr_warn("%s: empty cluster\n", cluster->full_name); + +	if (leaf) +		cluster_id++; + +	return 0; +} + +static int __init parse_dt_topology(void) +{ +	struct device_node *cn, *map; +	int ret = 0; +	int cpu; + +	cn = of_find_node_by_path("/cpus"); +	if (!cn) { +		pr_err("No CPU information found in DT\n"); +		return 0; +	} + +	/* +	 * When topology is provided cpu-map is essentially a root +	 * cluster with restricted subnodes. +	 */ +	map = of_get_child_by_name(cn, "cpu-map"); +	if (!map) +		goto out; + +	ret = parse_cluster(map, 0); +	if (ret != 0) +		goto out_map; + +	/* +	 * Check that all cores are in the topology; the SMP code will +	 * only mark cores described in the DT as possible. +	 */ +	for_each_possible_cpu(cpu) { +		if (cpu_topology[cpu].cluster_id == -1) { +			pr_err("CPU%d: No topology information specified\n", +			       cpu); +			ret = -EINVAL; +		} +	} + +out_map: +	of_node_put(map); +out: +	of_node_put(cn); +	return ret; +} + +/* + * cpu topology table + */ +struct cpu_topology cpu_topology[NR_CPUS]; +EXPORT_SYMBOL_GPL(cpu_topology); + +const struct cpumask *cpu_coregroup_mask(int cpu) +{ +	return &cpu_topology[cpu].core_sibling; +} + +static void update_siblings_masks(unsigned int cpuid) +{ +	struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid]; +	int cpu; + +	if (cpuid_topo->cluster_id == -1) { +		/* +		 * DT does not contain topology information for this cpu. +		 */ +		pr_debug("CPU%u: No topology information configured\n", cpuid); +		return; +	} + +	/* update core and thread sibling masks */ +	for_each_possible_cpu(cpu) { +		cpu_topo = &cpu_topology[cpu]; + +		if (cpuid_topo->cluster_id != cpu_topo->cluster_id) +			continue; + +		cpumask_set_cpu(cpuid, &cpu_topo->core_sibling); +		if (cpu != cpuid) +			cpumask_set_cpu(cpu, &cpuid_topo->core_sibling); + +		if (cpuid_topo->core_id != cpu_topo->core_id) +			continue; + +		cpumask_set_cpu(cpuid, &cpu_topo->thread_sibling); +		if (cpu != cpuid) +			cpumask_set_cpu(cpu, &cpuid_topo->thread_sibling); +	} +} + +void store_cpu_topology(unsigned int cpuid) +{ +	update_siblings_masks(cpuid); +} + +static void __init reset_cpu_topology(void) +{ +	unsigned int cpu; + +	for_each_possible_cpu(cpu) { +		struct cpu_topology *cpu_topo = &cpu_topology[cpu]; + +		cpu_topo->thread_id = -1; +		cpu_topo->core_id = 0; +		cpu_topo->cluster_id = -1; + +		cpumask_clear(&cpu_topo->core_sibling); +		cpumask_set_cpu(cpu, &cpu_topo->core_sibling); +		cpumask_clear(&cpu_topo->thread_sibling); +		cpumask_set_cpu(cpu, &cpu_topo->thread_sibling); +	} +} + +void __init init_cpu_topology(void) +{ +	reset_cpu_topology(); + +	/* +	 * Discard anything that was parsed if we hit an error so we +	 * don't use partial information. +	 */ +	if (parse_dt_topology()) +		reset_cpu_topology(); +} diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c index 7ffadddb645..c43cfa9b830 100644 --- a/arch/arm64/kernel/traps.c +++ b/arch/arm64/kernel/traps.c @@ -251,10 +251,13 @@ void die(const char *str, struct pt_regs *regs, int err)  void arm64_notify_die(const char *str, struct pt_regs *regs,  		      struct siginfo *info, int err)  { -	if (user_mode(regs)) +	if (user_mode(regs)) { +		current->thread.fault_address = 0; +		current->thread.fault_code = err;  		force_sig_info(info->si_signo, info, current); -	else +	} else {  		die(str, regs, err); +	}  }  asmlinkage void __exception do_undefinstr(struct pt_regs *regs) diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index 6a389dc1bd4..50384fec56c 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -58,7 +58,10 @@ static struct page *vectors_page[1];  static int alloc_vectors_page(void)  {  	extern char __kuser_helper_start[], __kuser_helper_end[]; +	extern char __aarch32_sigret_code_start[], __aarch32_sigret_code_end[]; +  	int kuser_sz = __kuser_helper_end - __kuser_helper_start; +	int sigret_sz = __aarch32_sigret_code_end - __aarch32_sigret_code_start;  	unsigned long vpage;  	vpage = get_zeroed_page(GFP_ATOMIC); @@ -72,7 +75,7 @@ static int alloc_vectors_page(void)  	/* sigreturn code */  	memcpy((void *)vpage + AARCH32_KERN_SIGRET_CODE_OFFSET, -		aarch32_sigret_code, sizeof(aarch32_sigret_code)); +               __aarch32_sigret_code_start, sigret_sz);  	flush_icache_range(vpage, vpage + PAGE_SIZE);  	vectors_page[0] = virt_to_page(vpage); @@ -103,49 +106,31 @@ int aarch32_setup_vectors_page(struct linux_binprm *bprm, int uses_interp)  static int __init vdso_init(void)  { -	struct page *pg; -	char *vbase; -	int i, ret = 0; +	int i; + +	if (memcmp(&vdso_start, "\177ELF", 4)) { +		pr_err("vDSO is not a valid ELF object!\n"); +		return -EINVAL; +	}  	vdso_pages = (&vdso_end - &vdso_start) >> PAGE_SHIFT;  	pr_info("vdso: %ld pages (%ld code, %ld data) at base %p\n",  		vdso_pages + 1, vdso_pages, 1L, &vdso_start);  	/* Allocate the vDSO pagelist, plus a page for the data. */ -	vdso_pagelist = kzalloc(sizeof(struct page *) * (vdso_pages + 1), +	vdso_pagelist = kcalloc(vdso_pages + 1, sizeof(struct page *),  				GFP_KERNEL); -	if (vdso_pagelist == NULL) { -		pr_err("Failed to allocate vDSO pagelist!\n"); +	if (vdso_pagelist == NULL)  		return -ENOMEM; -	}  	/* Grab the vDSO code pages. */ -	for (i = 0; i < vdso_pages; i++) { -		pg = virt_to_page(&vdso_start + i*PAGE_SIZE); -		ClearPageReserved(pg); -		get_page(pg); -		vdso_pagelist[i] = pg; -	} - -	/* Sanity check the shared object header. */ -	vbase = vmap(vdso_pagelist, 1, 0, PAGE_KERNEL); -	if (vbase == NULL) { -		pr_err("Failed to map vDSO pagelist!\n"); -		return -ENOMEM; -	} else if (memcmp(vbase, "\177ELF", 4)) { -		pr_err("vDSO is not a valid ELF object!\n"); -		ret = -EINVAL; -		goto unmap; -	} +	for (i = 0; i < vdso_pages; i++) +		vdso_pagelist[i] = virt_to_page(&vdso_start + i * PAGE_SIZE);  	/* Grab the vDSO data page. */ -	pg = virt_to_page(vdso_data); -	get_page(pg); -	vdso_pagelist[i] = pg; +	vdso_pagelist[i] = virt_to_page(vdso_data); -unmap: -	vunmap(vbase); -	return ret; +	return 0;  }  arch_initcall(vdso_init); @@ -235,6 +220,8 @@ void update_vsyscall(struct timekeeper *tk)  	vdso_data->use_syscall			= use_syscall;  	vdso_data->xtime_coarse_sec		= xtime_coarse.tv_sec;  	vdso_data->xtime_coarse_nsec		= xtime_coarse.tv_nsec; +	vdso_data->wtm_clock_sec		= tk->wall_to_monotonic.tv_sec; +	vdso_data->wtm_clock_nsec		= tk->wall_to_monotonic.tv_nsec;  	if (!use_syscall) {  		vdso_data->cs_cycle_last	= tk->clock->cycle_last; @@ -242,8 +229,6 @@ void update_vsyscall(struct timekeeper *tk)  		vdso_data->xtime_clock_nsec	= tk->xtime_nsec;  		vdso_data->cs_mult		= tk->mult;  		vdso_data->cs_shift		= tk->shift; -		vdso_data->wtm_clock_sec	= tk->wall_to_monotonic.tv_sec; -		vdso_data->wtm_clock_nsec	= tk->wall_to_monotonic.tv_nsec;  	}  	smp_wmb(); diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile index d8064af42e6..6d20b7d162d 100644 --- a/arch/arm64/kernel/vdso/Makefile +++ b/arch/arm64/kernel/vdso/Makefile @@ -48,7 +48,7 @@ $(obj-vdso): %.o: %.S  # Actual build commands  quiet_cmd_vdsold = VDSOL $@ -      cmd_vdsold = $(CC) $(c_flags) -Wl,-T $^ -o $@ +      cmd_vdsold = $(CC) $(c_flags) -Wl,-n -Wl,-T $^ -o $@  quiet_cmd_vdsoas = VDSOA $@        cmd_vdsoas = $(CC) $(a_flags) -c -o $@ $< diff --git a/arch/arm64/kernel/vdso/gettimeofday.S b/arch/arm64/kernel/vdso/gettimeofday.S index f0a6d10b521..fe652ffd34c 100644 --- a/arch/arm64/kernel/vdso/gettimeofday.S +++ b/arch/arm64/kernel/vdso/gettimeofday.S @@ -103,6 +103,8 @@ ENTRY(__kernel_clock_gettime)  	bl	__do_get_tspec  	seqcnt_check w9, 1b +	mov	x30, x2 +  	cmp	w0, #CLOCK_MONOTONIC  	b.ne	6f @@ -118,6 +120,9 @@ ENTRY(__kernel_clock_gettime)  	ccmp	w0, #CLOCK_MONOTONIC_COARSE, #0x4, ne  	b.ne	8f +	/* xtime_coarse_nsec is already right-shifted */ +	mov	x12, #0 +  	/* Get coarse timespec. */  	adr	vdso_data, _vdso_data  3:	seqcnt_acquire @@ -156,7 +161,7 @@ ENTRY(__kernel_clock_gettime)  	lsr	x11, x11, x12  	stp	x10, x11, [x1, #TSPEC_TV_SEC]  	mov	x0, xzr -	ret	x2 +	ret  7:  	mov	x30, x2  8:	/* Syscall fallback. */ diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S index f8ab9d8e2ea..f1e6d5c032e 100644 --- a/arch/arm64/kernel/vmlinux.lds.S +++ b/arch/arm64/kernel/vmlinux.lds.S @@ -13,7 +13,7 @@  #define ARM_EXIT_DISCARD(x)	x  OUTPUT_ARCH(aarch64) -ENTRY(stext) +ENTRY(_text)  jiffies = jiffies_64; @@ -54,7 +54,6 @@ SECTIONS  	}  	.text : {			/* Real text segment		*/  		_stext = .;		/* Text and read-only data	*/ -			*(.smp.pen.text)  			__exception_text_start = .;  			*(.exception.text)  			__exception_text_end = .; @@ -97,37 +96,17 @@ SECTIONS  	PERCPU_SECTION(64)  	__init_end = .; -	. = ALIGN(THREAD_SIZE); -	__data_loc = .; - -	.data : AT(__data_loc) { -		_data = .;		/* address in memory */ -		_sdata = .; - -		/* -		 * first, the init task union, aligned -		 * to an 8192 byte boundary. -		 */ -		INIT_TASK_DATA(THREAD_SIZE) -		NOSAVE_DATA -		CACHELINE_ALIGNED_DATA(64) -		READ_MOSTLY_DATA(64) - -		/* -		 * and the usual data section -		 */ -		DATA_DATA -		CONSTRUCTORS - -		_edata = .; -	} -	_edata_loc = __data_loc + SIZEOF(.data); + +	. = ALIGN(PAGE_SIZE); +	_data = .; +	_sdata = .; +	RW_DATA_SECTION(64, PAGE_SIZE, THREAD_SIZE) +	_edata = .;  	BSS_SECTION(0, 0, 0)  	_end = .;  	STABS_DEBUG -	.comment 0 : { *(.comment) }  }  /* diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig index 21e90820bd2..8ba85e9ea38 100644 --- a/arch/arm64/kvm/Kconfig +++ b/arch/arm64/kvm/Kconfig @@ -21,6 +21,7 @@ config KVM  	select MMU_NOTIFIER  	select PREEMPT_NOTIFIERS  	select ANON_INODES +	select HAVE_KVM_CPU_RELAX_INTERCEPT  	select KVM_MMIO  	select KVM_ARM_HOST  	select KVM_ARM_VGIC @@ -35,6 +36,17 @@ config KVM_ARM_HOST  	---help---  	  Provides host support for ARM processors. +config KVM_ARM_MAX_VCPUS +	int "Number maximum supported virtual CPUs per VM" +	depends on KVM_ARM_HOST +	default 4 +	help +	  Static number of max supported virtual CPUs per VM. + +	  If you choose a high number, the vcpu structures will be quite +	  large, so only choose a reasonable number that you expect to +	  actually use. +  config KVM_ARM_VGIC  	bool  	depends on KVM_ARM_HOST && OF diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c index 2c3ff67a8ec..60b5c31f3c1 100644 --- a/arch/arm64/kvm/guest.c +++ b/arch/arm64/kvm/guest.c @@ -207,20 +207,28 @@ int __attribute_const__ kvm_target_cpu(void)  	unsigned long implementor = read_cpuid_implementor();  	unsigned long part_number = read_cpuid_part_number(); -	if (implementor != ARM_CPU_IMP_ARM) -		return -EINVAL; +	switch (implementor) { +	case ARM_CPU_IMP_ARM: +		switch (part_number) { +		case ARM_CPU_PART_AEM_V8: +			return KVM_ARM_TARGET_AEM_V8; +		case ARM_CPU_PART_FOUNDATION: +			return KVM_ARM_TARGET_FOUNDATION_V8; +		case ARM_CPU_PART_CORTEX_A53: +			return KVM_ARM_TARGET_CORTEX_A53; +		case ARM_CPU_PART_CORTEX_A57: +			return KVM_ARM_TARGET_CORTEX_A57; +		}; +		break; +	case ARM_CPU_IMP_APM: +		switch (part_number) { +		case APM_CPU_PART_POTENZA: +			return KVM_ARM_TARGET_XGENE_POTENZA; +		}; +		break; +	}; -	switch (part_number) { -	case ARM_CPU_PART_AEM_V8: -		return KVM_ARM_TARGET_AEM_V8; -	case ARM_CPU_PART_FOUNDATION: -		return KVM_ARM_TARGET_FOUNDATION_V8; -	case ARM_CPU_PART_CORTEX_A57: -		/* Currently handled by the generic backend */ -		return KVM_ARM_TARGET_CORTEX_A57; -	default: -		return -EINVAL; -	} +	return -EINVAL;  }  int kvm_vcpu_set_target(struct kvm_vcpu *vcpu, @@ -248,6 +256,26 @@ int kvm_vcpu_set_target(struct kvm_vcpu *vcpu,  	return kvm_reset_vcpu(vcpu);  } +int kvm_vcpu_preferred_target(struct kvm_vcpu_init *init) +{ +	int target = kvm_target_cpu(); + +	if (target < 0) +		return -ENODEV; + +	memset(init, 0, sizeof(*init)); + +	/* +	 * For now, we don't return any features. +	 * In future, we might use features to return target +	 * specific features available for the preferred +	 * target type. +	 */ +	init->target = (__u32)target; + +	return 0; +} +  int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)  {  	return -EINVAL; diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c index 9beaca03343..182415e1a95 100644 --- a/arch/arm64/kvm/handle_exit.c +++ b/arch/arm64/kvm/handle_exit.c @@ -30,38 +30,47 @@ typedef int (*exit_handle_fn)(struct kvm_vcpu *, struct kvm_run *);  static int handle_hvc(struct kvm_vcpu *vcpu, struct kvm_run *run)  { -	if (kvm_psci_call(vcpu)) +	int ret; + +	ret = kvm_psci_call(vcpu); +	if (ret < 0) { +		kvm_inject_undefined(vcpu);  		return 1; +	} -	kvm_inject_undefined(vcpu); -	return 1; +	return ret;  }  static int handle_smc(struct kvm_vcpu *vcpu, struct kvm_run *run)  { -	if (kvm_psci_call(vcpu)) -		return 1; -  	kvm_inject_undefined(vcpu);  	return 1;  }  /** - * kvm_handle_wfi - handle a wait-for-interrupts instruction executed by a guest + * kvm_handle_wfx - handle a wait-for-interrupts or wait-for-event + *		    instruction executed by a guest + *   * @vcpu:	the vcpu pointer   * - * Simply call kvm_vcpu_block(), which will halt execution of + * WFE: Yield the CPU and come back to this vcpu when the scheduler + * decides to. + * WFI: Simply call kvm_vcpu_block(), which will halt execution of   * world-switches and schedule other host processes until there is an   * incoming IRQ or FIQ to the VM.   */ -static int kvm_handle_wfi(struct kvm_vcpu *vcpu, struct kvm_run *run) +static int kvm_handle_wfx(struct kvm_vcpu *vcpu, struct kvm_run *run)  { -	kvm_vcpu_block(vcpu); +	if (kvm_vcpu_get_hsr(vcpu) & ESR_EL2_EC_WFI_ISS_WFE) +		kvm_vcpu_on_spin(vcpu); +	else +		kvm_vcpu_block(vcpu); +  	return 1;  }  static exit_handle_fn arm_exit_handlers[] = { -	[ESR_EL2_EC_WFI]	= kvm_handle_wfi, +	[ESR_EL2_EC_WFI]	= kvm_handle_wfx,  	[ESR_EL2_EC_CP15_32]	= kvm_handle_cp15_32,  	[ESR_EL2_EC_CP15_64]	= kvm_handle_cp15_64,  	[ESR_EL2_EC_CP14_MR]	= kvm_handle_cp14_access, @@ -82,7 +91,7 @@ static exit_handle_fn kvm_get_exit_handler(struct kvm_vcpu *vcpu)  	if (hsr_ec >= ARRAY_SIZE(arm_exit_handlers) ||  	    !arm_exit_handlers[hsr_ec]) { -		kvm_err("Unkown exception class: hsr: %#08x\n", +		kvm_err("Unknown exception class: hsr: %#08x\n",  			(unsigned int)kvm_vcpu_get_hsr(vcpu));  		BUG();  	} diff --git a/arch/arm64/kvm/hyp-init.S b/arch/arm64/kvm/hyp-init.S index ba84e6705e2..d968796f4b2 100644 --- a/arch/arm64/kvm/hyp-init.S +++ b/arch/arm64/kvm/hyp-init.S @@ -68,13 +68,22 @@ __do_hyp_init:  	msr	tcr_el2, x4  	ldr	x4, =VTCR_EL2_FLAGS +	/* +	 * Read the PARange bits from ID_AA64MMFR0_EL1 and set the PS bits in +	 * VTCR_EL2. +	 */ +	mrs	x5, ID_AA64MMFR0_EL1 +	bfi	x4, x5, #16, #3  	msr	vtcr_el2, x4  	mrs	x4, mair_el1  	msr	mair_el2, x4  	isb -	mov	x4, #SCTLR_EL2_FLAGS +	mrs	x4, sctlr_el2 +	and	x4, x4, #SCTLR_EL2_EE	// preserve endianness of EL2 +	ldr	x5, =SCTLR_EL2_FLAGS +	orr	x4, x4, x5  	msr	sctlr_el2, x4  	isb diff --git a/arch/arm64/kvm/hyp.S b/arch/arm64/kvm/hyp.S index 1ac0bbbdddb..b0d1512acf0 100644 --- a/arch/arm64/kvm/hyp.S +++ b/arch/arm64/kvm/hyp.S @@ -403,6 +403,14 @@ __kvm_hyp_code_start:  	ldr	w9, [x2, #GICH_ELRSR0]  	ldr	w10, [x2, #GICH_ELRSR1]  	ldr	w11, [x2, #GICH_APR] +CPU_BE(	rev	w4,  w4  ) +CPU_BE(	rev	w5,  w5  ) +CPU_BE(	rev	w6,  w6  ) +CPU_BE(	rev	w7,  w7  ) +CPU_BE(	rev	w8,  w8  ) +CPU_BE(	rev	w9,  w9  ) +CPU_BE(	rev	w10, w10 ) +CPU_BE(	rev	w11, w11 )  	str	w4, [x3, #VGIC_CPU_HCR]  	str	w5, [x3, #VGIC_CPU_VMCR] @@ -421,6 +429,7 @@ __kvm_hyp_code_start:  	ldr	w4, [x3, #VGIC_CPU_NR_LR]  	add	x3, x3, #VGIC_CPU_LR  1:	ldr	w5, [x2], #4 +CPU_BE(	rev	w5, w5 )  	str	w5, [x3], #4  	sub	w4, w4, #1  	cbnz	w4, 1b @@ -446,6 +455,9 @@ __kvm_hyp_code_start:  	ldr	w4, [x3, #VGIC_CPU_HCR]  	ldr	w5, [x3, #VGIC_CPU_VMCR]  	ldr	w6, [x3, #VGIC_CPU_APR] +CPU_BE(	rev	w4, w4 ) +CPU_BE(	rev	w5, w5 ) +CPU_BE(	rev	w6, w6 )  	str	w4, [x2, #GICH_HCR]  	str	w5, [x2, #GICH_VMCR] @@ -456,6 +468,7 @@ __kvm_hyp_code_start:  	ldr	w4, [x3, #VGIC_CPU_NR_LR]  	add	x3, x3, #VGIC_CPU_LR  1:	ldr	w5, [x3], #4 +CPU_BE(	rev	w5, w5 )  	str	w5, [x2], #4  	sub	w4, w4, #1  	cbnz	w4, 1b @@ -617,9 +630,15 @@ ENTRY(__kvm_tlb_flush_vmid_ipa)  	 * whole of Stage-1. Weep...  	 */  	tlbi	ipas2e1is, x1 -	dsb	sy +	/* +	 * We have to ensure completion of the invalidation at Stage-2, +	 * since a table walk on another CPU could refill a TLB with a +	 * complete (S1 + S2) walk based on the old Stage-2 mapping if +	 * the Stage-1 invalidation happened first. +	 */ +	dsb	ish  	tlbi	vmalle1is -	dsb	sy +	dsb	ish  	isb  	msr	vttbr_el2, xzr @@ -630,7 +649,7 @@ ENTRY(__kvm_flush_vm_context)  	dsb	ishst  	tlbi	alle1is  	ic	ialluis -	dsb	sy +	dsb	ish  	ret  ENDPROC(__kvm_flush_vm_context) @@ -681,6 +700,24 @@ __hyp_panic_str:  	.align	2 +/* + * u64 kvm_call_hyp(void *hypfn, ...); + * + * This is not really a variadic function in the classic C-way and care must + * be taken when calling this to ensure parameters are passed in registers + * only, since the stack will change between the caller and the callee. + * + * Call the function with the first argument containing a pointer to the + * function you wish to call in Hyp mode, and subsequent arguments will be + * passed as x0, x1, and x2 (a maximum of 3 arguments in addition to the + * function pointer can be passed).  The function being called must be mapped + * in Hyp mode (see init_hyp_mode in arch/arm/kvm/arm.c).  Return values are + * passed in r0 and r1. + * + * A function pointer with a value of 0 has a special meaning, and is + * used to implement __hyp_get_vectors in the same way as in + * arch/arm64/kernel/hyp_stub.S. + */  ENTRY(kvm_call_hyp)  	hvc	#0  	ret @@ -724,7 +761,12 @@ el1_sync:					// Guest trapped into EL2  	pop	x2, x3  	pop	x0, x1 -	push	lr, xzr +	/* Check for __hyp_get_vectors */ +	cbnz	x0, 1f +	mrs	x0, vbar_el2 +	b	2f + +1:	push	lr, xzr  	/*  	 * Compute the function address in EL2, and shuffle the parameters. @@ -737,7 +779,7 @@ el1_sync:					// Guest trapped into EL2  	blr	lr  	pop	lr, xzr -	eret +2:	eret  el1_trap:  	/* diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 02e9d09e1d8..c59a1bdab5e 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -27,6 +27,7 @@  #include <asm/kvm_host.h>  #include <asm/kvm_emulate.h>  #include <asm/kvm_coproc.h> +#include <asm/kvm_mmu.h>  #include <asm/cacheflush.h>  #include <asm/cputype.h>  #include <trace/events/kvm.h> @@ -70,13 +71,13 @@ static u32 get_ccsidr(u32 csselr)  static void do_dc_cisw(u32 val)  {  	asm volatile("dc cisw, %x0" : : "r" (val)); -	dsb(); +	dsb(ish);  }  static void do_dc_csw(u32 val)  {  	asm volatile("dc csw, %x0" : : "r" (val)); -	dsb(); +	dsb(ish);  }  /* See note at ARM ARM B1.14.4 */ @@ -121,6 +122,48 @@ done:  }  /* + * Generic accessor for VM registers. Only called as long as HCR_TVM + * is set. + */ +static bool access_vm_reg(struct kvm_vcpu *vcpu, +			  const struct sys_reg_params *p, +			  const struct sys_reg_desc *r) +{ +	unsigned long val; + +	BUG_ON(!p->is_write); + +	val = *vcpu_reg(vcpu, p->Rt); +	if (!p->is_aarch32) { +		vcpu_sys_reg(vcpu, r->reg) = val; +	} else { +		vcpu_cp15(vcpu, r->reg) = val & 0xffffffffUL; +		if (!p->is_32bit) +			vcpu_cp15(vcpu, r->reg + 1) = val >> 32; +	} +	return true; +} + +/* + * SCTLR_EL1 accessor. Only called as long as HCR_TVM is set.  If the + * guest enables the MMU, we stop trapping the VM sys_regs and leave + * it in complete control of the caches. + */ +static bool access_sctlr(struct kvm_vcpu *vcpu, +			 const struct sys_reg_params *p, +			 const struct sys_reg_desc *r) +{ +	access_vm_reg(vcpu, p, r); + +	if (vcpu_has_cache_enabled(vcpu)) {	/* MMU+Caches enabled? */ +		vcpu->arch.hcr_el2 &= ~HCR_TVM; +		stage2_flush_vm(vcpu->kvm); +	} + +	return true; +} + +/*   * We could trap ID_DFR0 and tell the guest we don't support performance   * monitoring.  Unfortunately the patch to make the kernel check ID_DFR0 was   * NAKed, so it will read the PMCR anyway. @@ -185,32 +228,32 @@ static const struct sys_reg_desc sys_reg_descs[] = {  	  NULL, reset_mpidr, MPIDR_EL1 },  	/* SCTLR_EL1 */  	{ Op0(0b11), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b000), -	  NULL, reset_val, SCTLR_EL1, 0x00C50078 }, +	  access_sctlr, reset_val, SCTLR_EL1, 0x00C50078 },  	/* CPACR_EL1 */  	{ Op0(0b11), Op1(0b000), CRn(0b0001), CRm(0b0000), Op2(0b010),  	  NULL, reset_val, CPACR_EL1, 0 },  	/* TTBR0_EL1 */  	{ Op0(0b11), Op1(0b000), CRn(0b0010), CRm(0b0000), Op2(0b000), -	  NULL, reset_unknown, TTBR0_EL1 }, +	  access_vm_reg, reset_unknown, TTBR0_EL1 },  	/* TTBR1_EL1 */  	{ Op0(0b11), Op1(0b000), CRn(0b0010), CRm(0b0000), Op2(0b001), -	  NULL, reset_unknown, TTBR1_EL1 }, +	  access_vm_reg, reset_unknown, TTBR1_EL1 },  	/* TCR_EL1 */  	{ Op0(0b11), Op1(0b000), CRn(0b0010), CRm(0b0000), Op2(0b010), -	  NULL, reset_val, TCR_EL1, 0 }, +	  access_vm_reg, reset_val, TCR_EL1, 0 },  	/* AFSR0_EL1 */  	{ Op0(0b11), Op1(0b000), CRn(0b0101), CRm(0b0001), Op2(0b000), -	  NULL, reset_unknown, AFSR0_EL1 }, +	  access_vm_reg, reset_unknown, AFSR0_EL1 },  	/* AFSR1_EL1 */  	{ Op0(0b11), Op1(0b000), CRn(0b0101), CRm(0b0001), Op2(0b001), -	  NULL, reset_unknown, AFSR1_EL1 }, +	  access_vm_reg, reset_unknown, AFSR1_EL1 },  	/* ESR_EL1 */  	{ Op0(0b11), Op1(0b000), CRn(0b0101), CRm(0b0010), Op2(0b000), -	  NULL, reset_unknown, ESR_EL1 }, +	  access_vm_reg, reset_unknown, ESR_EL1 },  	/* FAR_EL1 */  	{ Op0(0b11), Op1(0b000), CRn(0b0110), CRm(0b0000), Op2(0b000), -	  NULL, reset_unknown, FAR_EL1 }, +	  access_vm_reg, reset_unknown, FAR_EL1 },  	/* PAR_EL1 */  	{ Op0(0b11), Op1(0b000), CRn(0b0111), CRm(0b0100), Op2(0b000),  	  NULL, reset_unknown, PAR_EL1 }, @@ -224,17 +267,17 @@ static const struct sys_reg_desc sys_reg_descs[] = {  	/* MAIR_EL1 */  	{ Op0(0b11), Op1(0b000), CRn(0b1010), CRm(0b0010), Op2(0b000), -	  NULL, reset_unknown, MAIR_EL1 }, +	  access_vm_reg, reset_unknown, MAIR_EL1 },  	/* AMAIR_EL1 */  	{ Op0(0b11), Op1(0b000), CRn(0b1010), CRm(0b0011), Op2(0b000), -	  NULL, reset_amair_el1, AMAIR_EL1 }, +	  access_vm_reg, reset_amair_el1, AMAIR_EL1 },  	/* VBAR_EL1 */  	{ Op0(0b11), Op1(0b000), CRn(0b1100), CRm(0b0000), Op2(0b000),  	  NULL, reset_val, VBAR_EL1, 0 },  	/* CONTEXTIDR_EL1 */  	{ Op0(0b11), Op1(0b000), CRn(0b1101), CRm(0b0000), Op2(0b001), -	  NULL, reset_val, CONTEXTIDR_EL1, 0 }, +	  access_vm_reg, reset_val, CONTEXTIDR_EL1, 0 },  	/* TPIDR_EL1 */  	{ Op0(0b11), Op1(0b000), CRn(0b1101), CRm(0b0000), Op2(0b100),  	  NULL, reset_unknown, TPIDR_EL1 }, @@ -305,14 +348,32 @@ static const struct sys_reg_desc sys_reg_descs[] = {  	  NULL, reset_val, FPEXC32_EL2, 0x70 },  }; -/* Trapped cp15 registers */ +/* + * Trapped cp15 registers. TTBR0/TTBR1 get a double encoding, + * depending on the way they are accessed (as a 32bit or a 64bit + * register). + */  static const struct sys_reg_desc cp15_regs[] = { +	{ Op1( 0), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, c2_TTBR0 }, +	{ Op1( 0), CRn( 1), CRm( 0), Op2( 0), access_sctlr, NULL, c1_SCTLR }, +	{ Op1( 0), CRn( 2), CRm( 0), Op2( 0), access_vm_reg, NULL, c2_TTBR0 }, +	{ Op1( 0), CRn( 2), CRm( 0), Op2( 1), access_vm_reg, NULL, c2_TTBR1 }, +	{ Op1( 0), CRn( 2), CRm( 0), Op2( 2), access_vm_reg, NULL, c2_TTBCR }, +	{ Op1( 0), CRn( 3), CRm( 0), Op2( 0), access_vm_reg, NULL, c3_DACR }, +	{ Op1( 0), CRn( 5), CRm( 0), Op2( 0), access_vm_reg, NULL, c5_DFSR }, +	{ Op1( 0), CRn( 5), CRm( 0), Op2( 1), access_vm_reg, NULL, c5_IFSR }, +	{ Op1( 0), CRn( 5), CRm( 1), Op2( 0), access_vm_reg, NULL, c5_ADFSR }, +	{ Op1( 0), CRn( 5), CRm( 1), Op2( 1), access_vm_reg, NULL, c5_AIFSR }, +	{ Op1( 0), CRn( 6), CRm( 0), Op2( 0), access_vm_reg, NULL, c6_DFAR }, +	{ Op1( 0), CRn( 6), CRm( 0), Op2( 2), access_vm_reg, NULL, c6_IFAR }, +  	/*  	 * DC{C,I,CI}SW operations:  	 */  	{ Op1( 0), CRn( 7), CRm( 6), Op2( 2), access_dcsw },  	{ Op1( 0), CRn( 7), CRm(10), Op2( 2), access_dcsw },  	{ Op1( 0), CRn( 7), CRm(14), Op2( 2), access_dcsw }, +  	{ Op1( 0), CRn( 9), CRm(12), Op2( 0), pm_fake },  	{ Op1( 0), CRn( 9), CRm(12), Op2( 1), pm_fake },  	{ Op1( 0), CRn( 9), CRm(12), Op2( 2), pm_fake }, @@ -326,6 +387,14 @@ static const struct sys_reg_desc cp15_regs[] = {  	{ Op1( 0), CRn( 9), CRm(14), Op2( 0), pm_fake },  	{ Op1( 0), CRn( 9), CRm(14), Op2( 1), pm_fake },  	{ Op1( 0), CRn( 9), CRm(14), Op2( 2), pm_fake }, + +	{ Op1( 0), CRn(10), CRm( 2), Op2( 0), access_vm_reg, NULL, c10_PRRR }, +	{ Op1( 0), CRn(10), CRm( 2), Op2( 1), access_vm_reg, NULL, c10_NMRR }, +	{ Op1( 0), CRn(10), CRm( 3), Op2( 0), access_vm_reg, NULL, c10_AMAIR0 }, +	{ Op1( 0), CRn(10), CRm( 3), Op2( 1), access_vm_reg, NULL, c10_AMAIR1 }, +	{ Op1( 0), CRn(13), CRm( 0), Op2( 1), access_vm_reg, NULL, c13_CID }, + +	{ Op1( 1), CRn( 0), CRm( 2), Op2( 0), access_vm_reg, NULL, c2_TTBR1 },  };  /* Target specific emulation tables */ @@ -437,6 +506,8 @@ int kvm_handle_cp15_64(struct kvm_vcpu *vcpu, struct kvm_run *run)  	u32 hsr = kvm_vcpu_get_hsr(vcpu);  	int Rt2 = (hsr >> 10) & 0xf; +	params.is_aarch32 = true; +	params.is_32bit = false;  	params.CRm = (hsr >> 1) & 0xf;  	params.Rt = (hsr >> 5) & 0xf;  	params.is_write = ((hsr & 1) == 0); @@ -480,6 +551,8 @@ int kvm_handle_cp15_32(struct kvm_vcpu *vcpu, struct kvm_run *run)  	struct sys_reg_params params;  	u32 hsr = kvm_vcpu_get_hsr(vcpu); +	params.is_aarch32 = true; +	params.is_32bit = true;  	params.CRm = (hsr >> 1) & 0xf;  	params.Rt  = (hsr >> 5) & 0xf;  	params.is_write = ((hsr & 1) == 0); @@ -549,6 +622,8 @@ int kvm_handle_sys_reg(struct kvm_vcpu *vcpu, struct kvm_run *run)  	struct sys_reg_params params;  	unsigned long esr = kvm_vcpu_get_hsr(vcpu); +	params.is_aarch32 = false; +	params.is_32bit = false;  	params.Op0 = (esr >> 20) & 3;  	params.Op1 = (esr >> 14) & 0x7;  	params.CRn = (esr >> 10) & 0xf; diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h index d50d3722998..d411e251412 100644 --- a/arch/arm64/kvm/sys_regs.h +++ b/arch/arm64/kvm/sys_regs.h @@ -30,6 +30,8 @@ struct sys_reg_params {  	u8	Op2;  	u8	Rt;  	bool	is_write; +	bool	is_aarch32; +	bool	is_32bit;	/* Only valid if is_aarch32 is true */  };  struct sys_reg_desc { diff --git a/arch/arm64/kvm/sys_regs_generic_v8.c b/arch/arm64/kvm/sys_regs_generic_v8.c index 4268ab9356b..475fd292931 100644 --- a/arch/arm64/kvm/sys_regs_generic_v8.c +++ b/arch/arm64/kvm/sys_regs_generic_v8.c @@ -88,8 +88,13 @@ static int __init sys_reg_genericv8_init(void)  					  &genericv8_target_table);  	kvm_register_target_sys_reg_table(KVM_ARM_TARGET_FOUNDATION_V8,  					  &genericv8_target_table); +	kvm_register_target_sys_reg_table(KVM_ARM_TARGET_CORTEX_A53, +					  &genericv8_target_table);  	kvm_register_target_sys_reg_table(KVM_ARM_TARGET_CORTEX_A57,  					  &genericv8_target_table); +	kvm_register_target_sys_reg_table(KVM_ARM_TARGET_XGENE_POTENZA, +					  &genericv8_target_table); +  	return 0;  }  late_initcall(sys_reg_genericv8_init); diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile index 59acc0ef046..d98d3e39879 100644 --- a/arch/arm64/lib/Makefile +++ b/arch/arm64/lib/Makefile @@ -1,6 +1,5 @@ -lib-y		:= bitops.o delay.o					\ -		   strncpy_from_user.o strnlen_user.o clear_user.o	\ -		   copy_from_user.o copy_to_user.o copy_in_user.o	\ -		   copy_page.o clear_page.o				\ -		   memchr.o memcpy.o memmove.o memset.o			\ +lib-y		:= bitops.o clear_user.o delay.o copy_from_user.o	\ +		   copy_to_user.o copy_in_user.o copy_page.o		\ +		   clear_page.o memchr.o memcpy.o memmove.o memset.o	\ +		   memcmp.o strcmp.o strncmp.o strlen.o strnlen.o	\  		   strchr.o strrchr.o diff --git a/arch/arm64/lib/bitops.S b/arch/arm64/lib/bitops.S index e5db797790d..7dac371cc9a 100644 --- a/arch/arm64/lib/bitops.S +++ b/arch/arm64/lib/bitops.S @@ -46,11 +46,12 @@ ENTRY(	\name	)  	mov	x2, #1  	add	x1, x1, x0, lsr #3	// Get word offset  	lsl	x4, x2, x3		// Create mask -1:	ldaxr	x2, [x1] +1:	ldxr	x2, [x1]  	lsr	x0, x2, x3		// Save old value of bit  	\instr	x2, x2, x4		// toggle bit  	stlxr	w5, x2, [x1]  	cbnz	w5, 1b +	dmb	ish  	and	x0, x0, #1  3:	ret  ENDPROC(\name	) diff --git a/arch/arm64/lib/memcmp.S b/arch/arm64/lib/memcmp.S new file mode 100644 index 00000000000..6ea0776ba6d --- /dev/null +++ b/arch/arm64/lib/memcmp.S @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2013 ARM Ltd. + * Copyright (C) 2013 Linaro. + * + * This code is based on glibc cortex strings work originally authored by Linaro + * and re-licensed under GPLv2 for the Linux kernel. The original code can + * be found @ + * + * http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/ + * files/head:/src/aarch64/ + * + * 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 <linux/linkage.h> +#include <asm/assembler.h> + +/* +* compare memory areas(when two memory areas' offset are different, +* alignment handled by the hardware) +* +* Parameters: +*  x0 - const memory area 1 pointer +*  x1 - const memory area 2 pointer +*  x2 - the maximal compare byte length +* Returns: +*  x0 - a compare result, maybe less than, equal to, or greater than ZERO +*/ + +/* Parameters and result.  */ +src1		.req	x0 +src2		.req	x1 +limit		.req	x2 +result		.req	x0 + +/* Internal variables.  */ +data1		.req	x3 +data1w		.req	w3 +data2		.req	x4 +data2w		.req	w4 +has_nul		.req	x5 +diff		.req	x6 +endloop		.req	x7 +tmp1		.req	x8 +tmp2		.req	x9 +tmp3		.req	x10 +pos		.req	x11 +limit_wd	.req	x12 +mask		.req	x13 + +ENTRY(memcmp) +	cbz	limit, .Lret0 +	eor	tmp1, src1, src2 +	tst	tmp1, #7 +	b.ne	.Lmisaligned8 +	ands	tmp1, src1, #7 +	b.ne	.Lmutual_align +	sub	limit_wd, limit, #1 /* limit != 0, so no underflow.  */ +	lsr	limit_wd, limit_wd, #3 /* Convert to Dwords.  */ +	/* +	* The input source addresses are at alignment boundary. +	* Directly compare eight bytes each time. +	*/ +.Lloop_aligned: +	ldr	data1, [src1], #8 +	ldr	data2, [src2], #8 +.Lstart_realigned: +	subs	limit_wd, limit_wd, #1 +	eor	diff, data1, data2	/* Non-zero if differences found.  */ +	csinv	endloop, diff, xzr, cs	/* Last Dword or differences.  */ +	cbz	endloop, .Lloop_aligned + +	/* Not reached the limit, must have found a diff.  */ +	tbz	limit_wd, #63, .Lnot_limit + +	/* Limit % 8 == 0 => the diff is in the last 8 bytes. */ +	ands	limit, limit, #7 +	b.eq	.Lnot_limit +	/* +	* The remained bytes less than 8. It is needed to extract valid data +	* from last eight bytes of the intended memory range. +	*/ +	lsl	limit, limit, #3	/* bytes-> bits.  */ +	mov	mask, #~0 +CPU_BE( lsr	mask, mask, limit ) +CPU_LE( lsl	mask, mask, limit ) +	bic	data1, data1, mask +	bic	data2, data2, mask + +	orr	diff, diff, mask +	b	.Lnot_limit + +.Lmutual_align: +	/* +	* Sources are mutually aligned, but are not currently at an +	* alignment boundary. Round down the addresses and then mask off +	* the bytes that precede the start point. +	*/ +	bic	src1, src1, #7 +	bic	src2, src2, #7 +	ldr	data1, [src1], #8 +	ldr	data2, [src2], #8 +	/* +	* We can not add limit with alignment offset(tmp1) here. Since the +	* addition probably make the limit overflown. +	*/ +	sub	limit_wd, limit, #1/*limit != 0, so no underflow.*/ +	and	tmp3, limit_wd, #7 +	lsr	limit_wd, limit_wd, #3 +	add	tmp3, tmp3, tmp1 +	add	limit_wd, limit_wd, tmp3, lsr #3 +	add	limit, limit, tmp1/* Adjust the limit for the extra.  */ + +	lsl	tmp1, tmp1, #3/* Bytes beyond alignment -> bits.*/ +	neg	tmp1, tmp1/* Bits to alignment -64.  */ +	mov	tmp2, #~0 +	/*mask off the non-intended bytes before the start address.*/ +CPU_BE( lsl	tmp2, tmp2, tmp1 )/*Big-endian.Early bytes are at MSB*/ +	/* Little-endian.  Early bytes are at LSB.  */ +CPU_LE( lsr	tmp2, tmp2, tmp1 ) + +	orr	data1, data1, tmp2 +	orr	data2, data2, tmp2 +	b	.Lstart_realigned + +	/*src1 and src2 have different alignment offset.*/ +.Lmisaligned8: +	cmp	limit, #8 +	b.lo	.Ltiny8proc /*limit < 8: compare byte by byte*/ + +	and	tmp1, src1, #7 +	neg	tmp1, tmp1 +	add	tmp1, tmp1, #8/*valid length in the first 8 bytes of src1*/ +	and	tmp2, src2, #7 +	neg	tmp2, tmp2 +	add	tmp2, tmp2, #8/*valid length in the first 8 bytes of src2*/ +	subs	tmp3, tmp1, tmp2 +	csel	pos, tmp1, tmp2, hi /*Choose the maximum.*/ + +	sub	limit, limit, pos +	/*compare the proceeding bytes in the first 8 byte segment.*/ +.Ltinycmp: +	ldrb	data1w, [src1], #1 +	ldrb	data2w, [src2], #1 +	subs	pos, pos, #1 +	ccmp	data1w, data2w, #0, ne  /* NZCV = 0b0000.  */ +	b.eq	.Ltinycmp +	cbnz	pos, 1f /*diff occurred before the last byte.*/ +	cmp	data1w, data2w +	b.eq	.Lstart_align +1: +	sub	result, data1, data2 +	ret + +.Lstart_align: +	lsr	limit_wd, limit, #3 +	cbz	limit_wd, .Lremain8 + +	ands	xzr, src1, #7 +	b.eq	.Lrecal_offset +	/*process more leading bytes to make src1 aligned...*/ +	add	src1, src1, tmp3 /*backwards src1 to alignment boundary*/ +	add	src2, src2, tmp3 +	sub	limit, limit, tmp3 +	lsr	limit_wd, limit, #3 +	cbz	limit_wd, .Lremain8 +	/*load 8 bytes from aligned SRC1..*/ +	ldr	data1, [src1], #8 +	ldr	data2, [src2], #8 + +	subs	limit_wd, limit_wd, #1 +	eor	diff, data1, data2  /*Non-zero if differences found.*/ +	csinv	endloop, diff, xzr, ne +	cbnz	endloop, .Lunequal_proc +	/*How far is the current SRC2 from the alignment boundary...*/ +	and	tmp3, tmp3, #7 + +.Lrecal_offset:/*src1 is aligned now..*/ +	neg	pos, tmp3 +.Lloopcmp_proc: +	/* +	* Divide the eight bytes into two parts. First,backwards the src2 +	* to an alignment boundary,load eight bytes and compare from +	* the SRC2 alignment boundary. If all 8 bytes are equal,then start +	* the second part's comparison. Otherwise finish the comparison. +	* This special handle can garantee all the accesses are in the +	* thread/task space in avoid to overrange access. +	*/ +	ldr	data1, [src1,pos] +	ldr	data2, [src2,pos] +	eor	diff, data1, data2  /* Non-zero if differences found.  */ +	cbnz	diff, .Lnot_limit + +	/*The second part process*/ +	ldr	data1, [src1], #8 +	ldr	data2, [src2], #8 +	eor	diff, data1, data2  /* Non-zero if differences found.  */ +	subs	limit_wd, limit_wd, #1 +	csinv	endloop, diff, xzr, ne/*if limit_wd is 0,will finish the cmp*/ +	cbz	endloop, .Lloopcmp_proc +.Lunequal_proc: +	cbz	diff, .Lremain8 + +/*There is differnence occured in the latest comparison.*/ +.Lnot_limit: +/* +* For little endian,reverse the low significant equal bits into MSB,then +* following CLZ can find how many equal bits exist. +*/ +CPU_LE( rev	diff, diff ) +CPU_LE( rev	data1, data1 ) +CPU_LE( rev	data2, data2 ) + +	/* +	* The MS-non-zero bit of DIFF marks either the first bit +	* that is different, or the end of the significant data. +	* Shifting left now will bring the critical information into the +	* top bits. +	*/ +	clz	pos, diff +	lsl	data1, data1, pos +	lsl	data2, data2, pos +	/* +	* We need to zero-extend (char is unsigned) the value and then +	* perform a signed subtraction. +	*/ +	lsr	data1, data1, #56 +	sub	result, data1, data2, lsr #56 +	ret + +.Lremain8: +	/* Limit % 8 == 0 =>. all data are equal.*/ +	ands	limit, limit, #7 +	b.eq	.Lret0 + +.Ltiny8proc: +	ldrb	data1w, [src1], #1 +	ldrb	data2w, [src2], #1 +	subs	limit, limit, #1 + +	ccmp	data1w, data2w, #0, ne  /* NZCV = 0b0000. */ +	b.eq	.Ltiny8proc +	sub	result, data1, data2 +	ret +.Lret0: +	mov	result, #0 +	ret +ENDPROC(memcmp) diff --git a/arch/arm64/lib/memcpy.S b/arch/arm64/lib/memcpy.S index 27b5003609b..8a9a96d3dda 100644 --- a/arch/arm64/lib/memcpy.S +++ b/arch/arm64/lib/memcpy.S @@ -1,5 +1,13 @@  /*   * Copyright (C) 2013 ARM Ltd. + * Copyright (C) 2013 Linaro. + * + * This code is based on glibc cortex strings work originally authored by Linaro + * and re-licensed under GPLv2 for the Linux kernel. The original code can + * be found @ + * + * http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/ + * files/head:/src/aarch64/   *   * 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 @@ -16,6 +24,7 @@  #include <linux/linkage.h>  #include <asm/assembler.h> +#include <asm/cache.h>  /*   * Copy a buffer from src to dest (alignment handled by the hardware) @@ -27,27 +36,166 @@   * Returns:   *	x0 - dest   */ +dstin	.req	x0 +src	.req	x1 +count	.req	x2 +tmp1	.req	x3 +tmp1w	.req	w3 +tmp2	.req	x4 +tmp2w	.req	w4 +tmp3	.req	x5 +tmp3w	.req	w5 +dst	.req	x6 + +A_l	.req	x7 +A_h	.req	x8 +B_l	.req	x9 +B_h	.req	x10 +C_l	.req	x11 +C_h	.req	x12 +D_l	.req	x13 +D_h	.req	x14 +  ENTRY(memcpy) -	mov	x4, x0 -	subs	x2, x2, #8 -	b.mi	2f -1:	ldr	x3, [x1], #8 -	subs	x2, x2, #8 -	str	x3, [x4], #8 -	b.pl	1b -2:	adds	x2, x2, #4 -	b.mi	3f -	ldr	w3, [x1], #4 -	sub	x2, x2, #4 -	str	w3, [x4], #4 -3:	adds	x2, x2, #2 -	b.mi	4f -	ldrh	w3, [x1], #2 -	sub	x2, x2, #2 -	strh	w3, [x4], #2 -4:	adds	x2, x2, #1 -	b.mi	5f -	ldrb	w3, [x1] -	strb	w3, [x4] -5:	ret +	mov	dst, dstin +	cmp	count, #16 +	/*When memory length is less than 16, the accessed are not aligned.*/ +	b.lo	.Ltiny15 + +	neg	tmp2, src +	ands	tmp2, tmp2, #15/* Bytes to reach alignment. */ +	b.eq	.LSrcAligned +	sub	count, count, tmp2 +	/* +	* Copy the leading memory data from src to dst in an increasing +	* address order.By this way,the risk of overwritting the source +	* memory data is eliminated when the distance between src and +	* dst is less than 16. The memory accesses here are alignment. +	*/ +	tbz	tmp2, #0, 1f +	ldrb	tmp1w, [src], #1 +	strb	tmp1w, [dst], #1 +1: +	tbz	tmp2, #1, 2f +	ldrh	tmp1w, [src], #2 +	strh	tmp1w, [dst], #2 +2: +	tbz	tmp2, #2, 3f +	ldr	tmp1w, [src], #4 +	str	tmp1w, [dst], #4 +3: +	tbz	tmp2, #3, .LSrcAligned +	ldr	tmp1, [src],#8 +	str	tmp1, [dst],#8 + +.LSrcAligned: +	cmp	count, #64 +	b.ge	.Lcpy_over64 +	/* +	* Deal with small copies quickly by dropping straight into the +	* exit block. +	*/ +.Ltail63: +	/* +	* Copy up to 48 bytes of data. At this point we only need the +	* bottom 6 bits of count to be accurate. +	*/ +	ands	tmp1, count, #0x30 +	b.eq	.Ltiny15 +	cmp	tmp1w, #0x20 +	b.eq	1f +	b.lt	2f +	ldp	A_l, A_h, [src], #16 +	stp	A_l, A_h, [dst], #16 +1: +	ldp	A_l, A_h, [src], #16 +	stp	A_l, A_h, [dst], #16 +2: +	ldp	A_l, A_h, [src], #16 +	stp	A_l, A_h, [dst], #16 +.Ltiny15: +	/* +	* Prefer to break one ldp/stp into several load/store to access +	* memory in an increasing address order,rather than to load/store 16 +	* bytes from (src-16) to (dst-16) and to backward the src to aligned +	* address,which way is used in original cortex memcpy. If keeping +	* the original memcpy process here, memmove need to satisfy the +	* precondition that src address is at least 16 bytes bigger than dst +	* address,otherwise some source data will be overwritten when memove +	* call memcpy directly. To make memmove simpler and decouple the +	* memcpy's dependency on memmove, withdrew the original process. +	*/ +	tbz	count, #3, 1f +	ldr	tmp1, [src], #8 +	str	tmp1, [dst], #8 +1: +	tbz	count, #2, 2f +	ldr	tmp1w, [src], #4 +	str	tmp1w, [dst], #4 +2: +	tbz	count, #1, 3f +	ldrh	tmp1w, [src], #2 +	strh	tmp1w, [dst], #2 +3: +	tbz	count, #0, .Lexitfunc +	ldrb	tmp1w, [src] +	strb	tmp1w, [dst] + +.Lexitfunc: +	ret + +.Lcpy_over64: +	subs	count, count, #128 +	b.ge	.Lcpy_body_large +	/* +	* Less than 128 bytes to copy, so handle 64 here and then jump +	* to the tail. +	*/ +	ldp	A_l, A_h, [src],#16 +	stp	A_l, A_h, [dst],#16 +	ldp	B_l, B_h, [src],#16 +	ldp	C_l, C_h, [src],#16 +	stp	B_l, B_h, [dst],#16 +	stp	C_l, C_h, [dst],#16 +	ldp	D_l, D_h, [src],#16 +	stp	D_l, D_h, [dst],#16 + +	tst	count, #0x3f +	b.ne	.Ltail63 +	ret + +	/* +	* Critical loop.  Start at a new cache line boundary.  Assuming +	* 64 bytes per line this ensures the entire loop is in one line. +	*/ +	.p2align	L1_CACHE_SHIFT +.Lcpy_body_large: +	/* pre-get 64 bytes data. */ +	ldp	A_l, A_h, [src],#16 +	ldp	B_l, B_h, [src],#16 +	ldp	C_l, C_h, [src],#16 +	ldp	D_l, D_h, [src],#16 +1: +	/* +	* interlace the load of next 64 bytes data block with store of the last +	* loaded 64 bytes data. +	*/ +	stp	A_l, A_h, [dst],#16 +	ldp	A_l, A_h, [src],#16 +	stp	B_l, B_h, [dst],#16 +	ldp	B_l, B_h, [src],#16 +	stp	C_l, C_h, [dst],#16 +	ldp	C_l, C_h, [src],#16 +	stp	D_l, D_h, [dst],#16 +	ldp	D_l, D_h, [src],#16 +	subs	count, count, #64 +	b.ge	1b +	stp	A_l, A_h, [dst],#16 +	stp	B_l, B_h, [dst],#16 +	stp	C_l, C_h, [dst],#16 +	stp	D_l, D_h, [dst],#16 + +	tst	count, #0x3f +	b.ne	.Ltail63 +	ret  ENDPROC(memcpy) diff --git a/arch/arm64/lib/memmove.S b/arch/arm64/lib/memmove.S index b79fdfa42d3..57b19ea2dad 100644 --- a/arch/arm64/lib/memmove.S +++ b/arch/arm64/lib/memmove.S @@ -1,5 +1,13 @@  /*   * Copyright (C) 2013 ARM Ltd. + * Copyright (C) 2013 Linaro. + * + * This code is based on glibc cortex strings work originally authored by Linaro + * and re-licensed under GPLv2 for the Linux kernel. The original code can + * be found @ + * + * http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/ + * files/head:/src/aarch64/   *   * 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 @@ -16,6 +24,7 @@  #include <linux/linkage.h>  #include <asm/assembler.h> +#include <asm/cache.h>  /*   * Move a buffer from src to test (alignment handled by the hardware). @@ -28,30 +37,161 @@   * Returns:   *	x0 - dest   */ +dstin	.req	x0 +src	.req	x1 +count	.req	x2 +tmp1	.req	x3 +tmp1w	.req	w3 +tmp2	.req	x4 +tmp2w	.req	w4 +tmp3	.req	x5 +tmp3w	.req	w5 +dst	.req	x6 + +A_l	.req	x7 +A_h	.req	x8 +B_l	.req	x9 +B_h	.req	x10 +C_l	.req	x11 +C_h	.req	x12 +D_l	.req	x13 +D_h	.req	x14 +  ENTRY(memmove) -	cmp	x0, x1 -	b.ls	memcpy -	add	x4, x0, x2 -	add	x1, x1, x2 -	subs	x2, x2, #8 -	b.mi	2f -1:	ldr	x3, [x1, #-8]! -	subs	x2, x2, #8 -	str	x3, [x4, #-8]! -	b.pl	1b -2:	adds	x2, x2, #4 -	b.mi	3f -	ldr	w3, [x1, #-4]! -	sub	x2, x2, #4 -	str	w3, [x4, #-4]! -3:	adds	x2, x2, #2 -	b.mi	4f -	ldrh	w3, [x1, #-2]! -	sub	x2, x2, #2 -	strh	w3, [x4, #-2]! -4:	adds	x2, x2, #1 -	b.mi	5f -	ldrb	w3, [x1, #-1] -	strb	w3, [x4, #-1] -5:	ret +	cmp	dstin, src +	b.lo	memcpy +	add	tmp1, src, count +	cmp	dstin, tmp1 +	b.hs	memcpy		/* No overlap.  */ + +	add	dst, dstin, count +	add	src, src, count +	cmp	count, #16 +	b.lo	.Ltail15  /*probably non-alignment accesses.*/ + +	ands	tmp2, src, #15     /* Bytes to reach alignment.  */ +	b.eq	.LSrcAligned +	sub	count, count, tmp2 +	/* +	* process the aligned offset length to make the src aligned firstly. +	* those extra instructions' cost is acceptable. It also make the +	* coming accesses are based on aligned address. +	*/ +	tbz	tmp2, #0, 1f +	ldrb	tmp1w, [src, #-1]! +	strb	tmp1w, [dst, #-1]! +1: +	tbz	tmp2, #1, 2f +	ldrh	tmp1w, [src, #-2]! +	strh	tmp1w, [dst, #-2]! +2: +	tbz	tmp2, #2, 3f +	ldr	tmp1w, [src, #-4]! +	str	tmp1w, [dst, #-4]! +3: +	tbz	tmp2, #3, .LSrcAligned +	ldr	tmp1, [src, #-8]! +	str	tmp1, [dst, #-8]! + +.LSrcAligned: +	cmp	count, #64 +	b.ge	.Lcpy_over64 + +	/* +	* Deal with small copies quickly by dropping straight into the +	* exit block. +	*/ +.Ltail63: +	/* +	* Copy up to 48 bytes of data. At this point we only need the +	* bottom 6 bits of count to be accurate. +	*/ +	ands	tmp1, count, #0x30 +	b.eq	.Ltail15 +	cmp	tmp1w, #0x20 +	b.eq	1f +	b.lt	2f +	ldp	A_l, A_h, [src, #-16]! +	stp	A_l, A_h, [dst, #-16]! +1: +	ldp	A_l, A_h, [src, #-16]! +	stp	A_l, A_h, [dst, #-16]! +2: +	ldp	A_l, A_h, [src, #-16]! +	stp	A_l, A_h, [dst, #-16]! + +.Ltail15: +	tbz	count, #3, 1f +	ldr	tmp1, [src, #-8]! +	str	tmp1, [dst, #-8]! +1: +	tbz	count, #2, 2f +	ldr	tmp1w, [src, #-4]! +	str	tmp1w, [dst, #-4]! +2: +	tbz	count, #1, 3f +	ldrh	tmp1w, [src, #-2]! +	strh	tmp1w, [dst, #-2]! +3: +	tbz	count, #0, .Lexitfunc +	ldrb	tmp1w, [src, #-1] +	strb	tmp1w, [dst, #-1] + +.Lexitfunc: +	ret + +.Lcpy_over64: +	subs	count, count, #128 +	b.ge	.Lcpy_body_large +	/* +	* Less than 128 bytes to copy, so handle 64 bytes here and then jump +	* to the tail. +	*/ +	ldp	A_l, A_h, [src, #-16] +	stp	A_l, A_h, [dst, #-16] +	ldp	B_l, B_h, [src, #-32] +	ldp	C_l, C_h, [src, #-48] +	stp	B_l, B_h, [dst, #-32] +	stp	C_l, C_h, [dst, #-48] +	ldp	D_l, D_h, [src, #-64]! +	stp	D_l, D_h, [dst, #-64]! + +	tst	count, #0x3f +	b.ne	.Ltail63 +	ret + +	/* +	* Critical loop. Start at a new cache line boundary. Assuming +	* 64 bytes per line this ensures the entire loop is in one line. +	*/ +	.p2align	L1_CACHE_SHIFT +.Lcpy_body_large: +	/* pre-load 64 bytes data. */ +	ldp	A_l, A_h, [src, #-16] +	ldp	B_l, B_h, [src, #-32] +	ldp	C_l, C_h, [src, #-48] +	ldp	D_l, D_h, [src, #-64]! +1: +	/* +	* interlace the load of next 64 bytes data block with store of the last +	* loaded 64 bytes data. +	*/ +	stp	A_l, A_h, [dst, #-16] +	ldp	A_l, A_h, [src, #-16] +	stp	B_l, B_h, [dst, #-32] +	ldp	B_l, B_h, [src, #-32] +	stp	C_l, C_h, [dst, #-48] +	ldp	C_l, C_h, [src, #-48] +	stp	D_l, D_h, [dst, #-64]! +	ldp	D_l, D_h, [src, #-64]! +	subs	count, count, #64 +	b.ge	1b +	stp	A_l, A_h, [dst, #-16] +	stp	B_l, B_h, [dst, #-32] +	stp	C_l, C_h, [dst, #-48] +	stp	D_l, D_h, [dst, #-64]! + +	tst	count, #0x3f +	b.ne	.Ltail63 +	ret  ENDPROC(memmove) diff --git a/arch/arm64/lib/memset.S b/arch/arm64/lib/memset.S index 87e4a68fbbb..7c72dfd36b6 100644 --- a/arch/arm64/lib/memset.S +++ b/arch/arm64/lib/memset.S @@ -1,5 +1,13 @@  /*   * Copyright (C) 2013 ARM Ltd. + * Copyright (C) 2013 Linaro. + * + * This code is based on glibc cortex strings work originally authored by Linaro + * and re-licensed under GPLv2 for the Linux kernel. The original code can + * be found @ + * + * http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/ + * files/head:/src/aarch64/   *   * 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 @@ -16,6 +24,7 @@  #include <linux/linkage.h>  #include <asm/assembler.h> +#include <asm/cache.h>  /*   * Fill in the buffer with character c (alignment handled by the hardware) @@ -27,27 +36,181 @@   * Returns:   *	x0 - buf   */ + +dstin		.req	x0 +val		.req	w1 +count		.req	x2 +tmp1		.req	x3 +tmp1w		.req	w3 +tmp2		.req	x4 +tmp2w		.req	w4 +zva_len_x	.req	x5 +zva_len		.req	w5 +zva_bits_x	.req	x6 + +A_l		.req	x7 +A_lw		.req	w7 +dst		.req	x8 +tmp3w		.req	w9 +tmp3		.req	x9 +  ENTRY(memset) -	mov	x4, x0 -	and	w1, w1, #0xff -	orr	w1, w1, w1, lsl #8 -	orr	w1, w1, w1, lsl #16 -	orr	x1, x1, x1, lsl #32 -	subs	x2, x2, #8 -	b.mi	2f -1:	str	x1, [x4], #8 -	subs	x2, x2, #8 -	b.pl	1b -2:	adds	x2, x2, #4 -	b.mi	3f -	sub	x2, x2, #4 -	str	w1, [x4], #4 -3:	adds	x2, x2, #2 -	b.mi	4f -	sub	x2, x2, #2 -	strh	w1, [x4], #2 -4:	adds	x2, x2, #1 -	b.mi	5f -	strb	w1, [x4] -5:	ret +	mov	dst, dstin	/* Preserve return value.  */ +	and	A_lw, val, #255 +	orr	A_lw, A_lw, A_lw, lsl #8 +	orr	A_lw, A_lw, A_lw, lsl #16 +	orr	A_l, A_l, A_l, lsl #32 + +	cmp	count, #15 +	b.hi	.Lover16_proc +	/*All store maybe are non-aligned..*/ +	tbz	count, #3, 1f +	str	A_l, [dst], #8 +1: +	tbz	count, #2, 2f +	str	A_lw, [dst], #4 +2: +	tbz	count, #1, 3f +	strh	A_lw, [dst], #2 +3: +	tbz	count, #0, 4f +	strb	A_lw, [dst] +4: +	ret + +.Lover16_proc: +	/*Whether  the start address is aligned with 16.*/ +	neg	tmp2, dst +	ands	tmp2, tmp2, #15 +	b.eq	.Laligned +/* +* The count is not less than 16, we can use stp to store the start 16 bytes, +* then adjust the dst aligned with 16.This process will make the current +* memory address at alignment boundary. +*/ +	stp	A_l, A_l, [dst] /*non-aligned store..*/ +	/*make the dst aligned..*/ +	sub	count, count, tmp2 +	add	dst, dst, tmp2 + +.Laligned: +	cbz	A_l, .Lzero_mem + +.Ltail_maybe_long: +	cmp	count, #64 +	b.ge	.Lnot_short +.Ltail63: +	ands	tmp1, count, #0x30 +	b.eq	3f +	cmp	tmp1w, #0x20 +	b.eq	1f +	b.lt	2f +	stp	A_l, A_l, [dst], #16 +1: +	stp	A_l, A_l, [dst], #16 +2: +	stp	A_l, A_l, [dst], #16 +/* +* The last store length is less than 16,use stp to write last 16 bytes. +* It will lead some bytes written twice and the access is non-aligned. +*/ +3: +	ands	count, count, #15 +	cbz	count, 4f +	add	dst, dst, count +	stp	A_l, A_l, [dst, #-16]	/* Repeat some/all of last store. */ +4: +	ret + +	/* +	* Critical loop. Start at a new cache line boundary. Assuming +	* 64 bytes per line, this ensures the entire loop is in one line. +	*/ +	.p2align	L1_CACHE_SHIFT +.Lnot_short: +	sub	dst, dst, #16/* Pre-bias.  */ +	sub	count, count, #64 +1: +	stp	A_l, A_l, [dst, #16] +	stp	A_l, A_l, [dst, #32] +	stp	A_l, A_l, [dst, #48] +	stp	A_l, A_l, [dst, #64]! +	subs	count, count, #64 +	b.ge	1b +	tst	count, #0x3f +	add	dst, dst, #16 +	b.ne	.Ltail63 +.Lexitfunc: +	ret + +	/* +	* For zeroing memory, check to see if we can use the ZVA feature to +	* zero entire 'cache' lines. +	*/ +.Lzero_mem: +	cmp	count, #63 +	b.le	.Ltail63 +	/* +	* For zeroing small amounts of memory, it's not worth setting up +	* the line-clear code. +	*/ +	cmp	count, #128 +	b.lt	.Lnot_short /*count is at least  128 bytes*/ + +	mrs	tmp1, dczid_el0 +	tbnz	tmp1, #4, .Lnot_short +	mov	tmp3w, #4 +	and	zva_len, tmp1w, #15	/* Safety: other bits reserved.  */ +	lsl	zva_len, tmp3w, zva_len + +	ands	tmp3w, zva_len, #63 +	/* +	* ensure the zva_len is not less than 64. +	* It is not meaningful to use ZVA if the block size is less than 64. +	*/ +	b.ne	.Lnot_short +.Lzero_by_line: +	/* +	* Compute how far we need to go to become suitably aligned. We're +	* already at quad-word alignment. +	*/ +	cmp	count, zva_len_x +	b.lt	.Lnot_short		/* Not enough to reach alignment.  */ +	sub	zva_bits_x, zva_len_x, #1 +	neg	tmp2, dst +	ands	tmp2, tmp2, zva_bits_x +	b.eq	2f			/* Already aligned.  */ +	/* Not aligned, check that there's enough to copy after alignment.*/ +	sub	tmp1, count, tmp2 +	/* +	* grantee the remain length to be ZVA is bigger than 64, +	* avoid to make the 2f's process over mem range.*/ +	cmp	tmp1, #64 +	ccmp	tmp1, zva_len_x, #8, ge	/* NZCV=0b1000 */ +	b.lt	.Lnot_short +	/* +	* We know that there's at least 64 bytes to zero and that it's safe +	* to overrun by 64 bytes. +	*/ +	mov	count, tmp1 +1: +	stp	A_l, A_l, [dst] +	stp	A_l, A_l, [dst, #16] +	stp	A_l, A_l, [dst, #32] +	subs	tmp2, tmp2, #64 +	stp	A_l, A_l, [dst, #48] +	add	dst, dst, #64 +	b.ge	1b +	/* We've overrun a bit, so adjust dst downwards.*/ +	add	dst, dst, tmp2 +2: +	sub	count, count, zva_len_x +3: +	dc	zva, dst +	add	dst, dst, zva_len_x +	subs	count, count, zva_len_x +	b.ge	3b +	ands	count, count, zva_bits_x +	b.ne	.Ltail_maybe_long +	ret  ENDPROC(memset) diff --git a/arch/arm64/lib/strcmp.S b/arch/arm64/lib/strcmp.S new file mode 100644 index 00000000000..42f828b06c5 --- /dev/null +++ b/arch/arm64/lib/strcmp.S @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2013 ARM Ltd. + * Copyright (C) 2013 Linaro. + * + * This code is based on glibc cortex strings work originally authored by Linaro + * and re-licensed under GPLv2 for the Linux kernel. The original code can + * be found @ + * + * http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/ + * files/head:/src/aarch64/ + * + * 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 <linux/linkage.h> +#include <asm/assembler.h> + +/* + * compare two strings + * + * Parameters: + *	x0 - const string 1 pointer + *    x1 - const string 2 pointer + * Returns: + * x0 - an integer less than, equal to, or greater than zero + * if  s1  is  found, respectively, to be less than, to match, + * or be greater than s2. + */ + +#define REP8_01 0x0101010101010101 +#define REP8_7f 0x7f7f7f7f7f7f7f7f +#define REP8_80 0x8080808080808080 + +/* Parameters and result.  */ +src1		.req	x0 +src2		.req	x1 +result		.req	x0 + +/* Internal variables.  */ +data1		.req	x2 +data1w		.req	w2 +data2		.req	x3 +data2w		.req	w3 +has_nul		.req	x4 +diff		.req	x5 +syndrome	.req	x6 +tmp1		.req	x7 +tmp2		.req	x8 +tmp3		.req	x9 +zeroones	.req	x10 +pos		.req	x11 + +ENTRY(strcmp) +	eor	tmp1, src1, src2 +	mov	zeroones, #REP8_01 +	tst	tmp1, #7 +	b.ne	.Lmisaligned8 +	ands	tmp1, src1, #7 +	b.ne	.Lmutual_align + +	/* +	* NUL detection works on the principle that (X - 1) & (~X) & 0x80 +	* (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and +	* can be done in parallel across the entire word. +	*/ +.Lloop_aligned: +	ldr	data1, [src1], #8 +	ldr	data2, [src2], #8 +.Lstart_realigned: +	sub	tmp1, data1, zeroones +	orr	tmp2, data1, #REP8_7f +	eor	diff, data1, data2	/* Non-zero if differences found.  */ +	bic	has_nul, tmp1, tmp2	/* Non-zero if NUL terminator.  */ +	orr	syndrome, diff, has_nul +	cbz	syndrome, .Lloop_aligned +	b	.Lcal_cmpresult + +.Lmutual_align: +	/* +	* Sources are mutually aligned, but are not currently at an +	* alignment boundary.  Round down the addresses and then mask off +	* the bytes that preceed the start point. +	*/ +	bic	src1, src1, #7 +	bic	src2, src2, #7 +	lsl	tmp1, tmp1, #3		/* Bytes beyond alignment -> bits.  */ +	ldr	data1, [src1], #8 +	neg	tmp1, tmp1		/* Bits to alignment -64.  */ +	ldr	data2, [src2], #8 +	mov	tmp2, #~0 +	/* Big-endian.  Early bytes are at MSB.  */ +CPU_BE( lsl	tmp2, tmp2, tmp1 )	/* Shift (tmp1 & 63).  */ +	/* Little-endian.  Early bytes are at LSB.  */ +CPU_LE( lsr	tmp2, tmp2, tmp1 )	/* Shift (tmp1 & 63).  */ + +	orr	data1, data1, tmp2 +	orr	data2, data2, tmp2 +	b	.Lstart_realigned + +.Lmisaligned8: +	/* +	* Get the align offset length to compare per byte first. +	* After this process, one string's address will be aligned. +	*/ +	and	tmp1, src1, #7 +	neg	tmp1, tmp1 +	add	tmp1, tmp1, #8 +	and	tmp2, src2, #7 +	neg	tmp2, tmp2 +	add	tmp2, tmp2, #8 +	subs	tmp3, tmp1, tmp2 +	csel	pos, tmp1, tmp2, hi /*Choose the maximum. */ +.Ltinycmp: +	ldrb	data1w, [src1], #1 +	ldrb	data2w, [src2], #1 +	subs	pos, pos, #1 +	ccmp	data1w, #1, #0, ne  /* NZCV = 0b0000.  */ +	ccmp	data1w, data2w, #0, cs  /* NZCV = 0b0000.  */ +	b.eq	.Ltinycmp +	cbnz	pos, 1f /*find the null or unequal...*/ +	cmp	data1w, #1 +	ccmp	data1w, data2w, #0, cs +	b.eq	.Lstart_align /*the last bytes are equal....*/ +1: +	sub	result, data1, data2 +	ret + +.Lstart_align: +	ands	xzr, src1, #7 +	b.eq	.Lrecal_offset +	/*process more leading bytes to make str1 aligned...*/ +	add	src1, src1, tmp3 +	add	src2, src2, tmp3 +	/*load 8 bytes from aligned str1 and non-aligned str2..*/ +	ldr	data1, [src1], #8 +	ldr	data2, [src2], #8 + +	sub	tmp1, data1, zeroones +	orr	tmp2, data1, #REP8_7f +	bic	has_nul, tmp1, tmp2 +	eor	diff, data1, data2 /* Non-zero if differences found.  */ +	orr	syndrome, diff, has_nul +	cbnz	syndrome, .Lcal_cmpresult +	/*How far is the current str2 from the alignment boundary...*/ +	and	tmp3, tmp3, #7 +.Lrecal_offset: +	neg	pos, tmp3 +.Lloopcmp_proc: +	/* +	* Divide the eight bytes into two parts. First,backwards the src2 +	* to an alignment boundary,load eight bytes from the SRC2 alignment +	* boundary,then compare with the relative bytes from SRC1. +	* If all 8 bytes are equal,then start the second part's comparison. +	* Otherwise finish the comparison. +	* This special handle can garantee all the accesses are in the +	* thread/task space in avoid to overrange access. +	*/ +	ldr	data1, [src1,pos] +	ldr	data2, [src2,pos] +	sub	tmp1, data1, zeroones +	orr	tmp2, data1, #REP8_7f +	bic	has_nul, tmp1, tmp2 +	eor	diff, data1, data2  /* Non-zero if differences found.  */ +	orr	syndrome, diff, has_nul +	cbnz	syndrome, .Lcal_cmpresult + +	/*The second part process*/ +	ldr	data1, [src1], #8 +	ldr	data2, [src2], #8 +	sub	tmp1, data1, zeroones +	orr	tmp2, data1, #REP8_7f +	bic	has_nul, tmp1, tmp2 +	eor	diff, data1, data2  /* Non-zero if differences found.  */ +	orr	syndrome, diff, has_nul +	cbz	syndrome, .Lloopcmp_proc + +.Lcal_cmpresult: +	/* +	* reversed the byte-order as big-endian,then CLZ can find the most +	* significant zero bits. +	*/ +CPU_LE( rev	syndrome, syndrome ) +CPU_LE( rev	data1, data1 ) +CPU_LE( rev	data2, data2 ) + +	/* +	* For big-endian we cannot use the trick with the syndrome value +	* as carry-propagation can corrupt the upper bits if the trailing +	* bytes in the string contain 0x01. +	* However, if there is no NUL byte in the dword, we can generate +	* the result directly.  We ca not just subtract the bytes as the +	* MSB might be significant. +	*/ +CPU_BE( cbnz	has_nul, 1f ) +CPU_BE( cmp	data1, data2 ) +CPU_BE( cset	result, ne ) +CPU_BE( cneg	result, result, lo ) +CPU_BE( ret ) +CPU_BE( 1: ) +	/*Re-compute the NUL-byte detection, using a byte-reversed value. */ +CPU_BE(	rev	tmp3, data1 ) +CPU_BE(	sub	tmp1, tmp3, zeroones ) +CPU_BE(	orr	tmp2, tmp3, #REP8_7f ) +CPU_BE(	bic	has_nul, tmp1, tmp2 ) +CPU_BE(	rev	has_nul, has_nul ) +CPU_BE(	orr	syndrome, diff, has_nul ) + +	clz	pos, syndrome +	/* +	* The MS-non-zero bit of the syndrome marks either the first bit +	* that is different, or the top bit of the first zero byte. +	* Shifting left now will bring the critical information into the +	* top bits. +	*/ +	lsl	data1, data1, pos +	lsl	data2, data2, pos +	/* +	* But we need to zero-extend (char is unsigned) the value and then +	* perform a signed 32-bit subtraction. +	*/ +	lsr	data1, data1, #56 +	sub	result, data1, data2, lsr #56 +	ret +ENDPROC(strcmp) diff --git a/arch/arm64/lib/strlen.S b/arch/arm64/lib/strlen.S new file mode 100644 index 00000000000..987b68b9ce4 --- /dev/null +++ b/arch/arm64/lib/strlen.S @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2013 ARM Ltd. + * Copyright (C) 2013 Linaro. + * + * This code is based on glibc cortex strings work originally authored by Linaro + * and re-licensed under GPLv2 for the Linux kernel. The original code can + * be found @ + * + * http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/ + * files/head:/src/aarch64/ + * + * 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 <linux/linkage.h> +#include <asm/assembler.h> + +/* + * calculate the length of a string + * + * Parameters: + *	x0 - const string pointer + * Returns: + *	x0 - the return length of specific string + */ + +/* Arguments and results.  */ +srcin		.req	x0 +len		.req	x0 + +/* Locals and temporaries.  */ +src		.req	x1 +data1		.req	x2 +data2		.req	x3 +data2a		.req	x4 +has_nul1	.req	x5 +has_nul2	.req	x6 +tmp1		.req	x7 +tmp2		.req	x8 +tmp3		.req	x9 +tmp4		.req	x10 +zeroones	.req	x11 +pos		.req	x12 + +#define REP8_01 0x0101010101010101 +#define REP8_7f 0x7f7f7f7f7f7f7f7f +#define REP8_80 0x8080808080808080 + +ENTRY(strlen) +	mov	zeroones, #REP8_01 +	bic	src, srcin, #15 +	ands	tmp1, srcin, #15 +	b.ne	.Lmisaligned +	/* +	* NUL detection works on the principle that (X - 1) & (~X) & 0x80 +	* (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and +	* can be done in parallel across the entire word. +	*/ +	/* +	* The inner loop deals with two Dwords at a time. This has a +	* slightly higher start-up cost, but we should win quite quickly, +	* especially on cores with a high number of issue slots per +	* cycle, as we get much better parallelism out of the operations. +	*/ +.Lloop: +	ldp	data1, data2, [src], #16 +.Lrealigned: +	sub	tmp1, data1, zeroones +	orr	tmp2, data1, #REP8_7f +	sub	tmp3, data2, zeroones +	orr	tmp4, data2, #REP8_7f +	bic	has_nul1, tmp1, tmp2 +	bics	has_nul2, tmp3, tmp4 +	ccmp	has_nul1, #0, #0, eq	/* NZCV = 0000  */ +	b.eq	.Lloop + +	sub	len, src, srcin +	cbz	has_nul1, .Lnul_in_data2 +CPU_BE(	mov	data2, data1 )	/*prepare data to re-calculate the syndrome*/ +	sub	len, len, #8 +	mov	has_nul2, has_nul1 +.Lnul_in_data2: +	/* +	* For big-endian, carry propagation (if the final byte in the +	* string is 0x01) means we cannot use has_nul directly.  The +	* easiest way to get the correct byte is to byte-swap the data +	* and calculate the syndrome a second time. +	*/ +CPU_BE( rev	data2, data2 ) +CPU_BE( sub	tmp1, data2, zeroones ) +CPU_BE( orr	tmp2, data2, #REP8_7f ) +CPU_BE( bic	has_nul2, tmp1, tmp2 ) + +	sub	len, len, #8 +	rev	has_nul2, has_nul2 +	clz	pos, has_nul2 +	add	len, len, pos, lsr #3		/* Bits to bytes.  */ +	ret + +.Lmisaligned: +	cmp	tmp1, #8 +	neg	tmp1, tmp1 +	ldp	data1, data2, [src], #16 +	lsl	tmp1, tmp1, #3		/* Bytes beyond alignment -> bits.  */ +	mov	tmp2, #~0 +	/* Big-endian.  Early bytes are at MSB.  */ +CPU_BE( lsl	tmp2, tmp2, tmp1 )	/* Shift (tmp1 & 63).  */ +	/* Little-endian.  Early bytes are at LSB.  */ +CPU_LE( lsr	tmp2, tmp2, tmp1 )	/* Shift (tmp1 & 63).  */ + +	orr	data1, data1, tmp2 +	orr	data2a, data2, tmp2 +	csinv	data1, data1, xzr, le +	csel	data2, data2, data2a, le +	b	.Lrealigned +ENDPROC(strlen) diff --git a/arch/arm64/lib/strncmp.S b/arch/arm64/lib/strncmp.S new file mode 100644 index 00000000000..0224cf5a553 --- /dev/null +++ b/arch/arm64/lib/strncmp.S @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2013 ARM Ltd. + * Copyright (C) 2013 Linaro. + * + * This code is based on glibc cortex strings work originally authored by Linaro + * and re-licensed under GPLv2 for the Linux kernel. The original code can + * be found @ + * + * http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/ + * files/head:/src/aarch64/ + * + * 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 <linux/linkage.h> +#include <asm/assembler.h> + +/* + * compare two strings + * + * Parameters: + *  x0 - const string 1 pointer + *  x1 - const string 2 pointer + *  x2 - the maximal length to be compared + * Returns: + *  x0 - an integer less than, equal to, or greater than zero if s1 is found, + *     respectively, to be less than, to match, or be greater than s2. + */ + +#define REP8_01 0x0101010101010101 +#define REP8_7f 0x7f7f7f7f7f7f7f7f +#define REP8_80 0x8080808080808080 + +/* Parameters and result.  */ +src1		.req	x0 +src2		.req	x1 +limit		.req	x2 +result		.req	x0 + +/* Internal variables.  */ +data1		.req	x3 +data1w		.req	w3 +data2		.req	x4 +data2w		.req	w4 +has_nul		.req	x5 +diff		.req	x6 +syndrome	.req	x7 +tmp1		.req	x8 +tmp2		.req	x9 +tmp3		.req	x10 +zeroones	.req	x11 +pos		.req	x12 +limit_wd	.req	x13 +mask		.req	x14 +endloop		.req	x15 + +ENTRY(strncmp) +	cbz	limit, .Lret0 +	eor	tmp1, src1, src2 +	mov	zeroones, #REP8_01 +	tst	tmp1, #7 +	b.ne	.Lmisaligned8 +	ands	tmp1, src1, #7 +	b.ne	.Lmutual_align +	/* Calculate the number of full and partial words -1.  */ +	/* +	* when limit is mulitply of 8, if not sub 1, +	* the judgement of last dword will wrong. +	*/ +	sub	limit_wd, limit, #1 /* limit != 0, so no underflow.  */ +	lsr	limit_wd, limit_wd, #3  /* Convert to Dwords.  */ + +	/* +	* NUL detection works on the principle that (X - 1) & (~X) & 0x80 +	* (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and +	* can be done in parallel across the entire word. +	*/ +.Lloop_aligned: +	ldr	data1, [src1], #8 +	ldr	data2, [src2], #8 +.Lstart_realigned: +	subs	limit_wd, limit_wd, #1 +	sub	tmp1, data1, zeroones +	orr	tmp2, data1, #REP8_7f +	eor	diff, data1, data2  /* Non-zero if differences found.  */ +	csinv	endloop, diff, xzr, pl  /* Last Dword or differences.*/ +	bics	has_nul, tmp1, tmp2 /* Non-zero if NUL terminator.  */ +	ccmp	endloop, #0, #0, eq +	b.eq	.Lloop_aligned + +	/*Not reached the limit, must have found the end or a diff.  */ +	tbz	limit_wd, #63, .Lnot_limit + +	/* Limit % 8 == 0 => all bytes significant.  */ +	ands	limit, limit, #7 +	b.eq	.Lnot_limit + +	lsl	limit, limit, #3    /* Bits -> bytes.  */ +	mov	mask, #~0 +CPU_BE( lsr	mask, mask, limit ) +CPU_LE( lsl	mask, mask, limit ) +	bic	data1, data1, mask +	bic	data2, data2, mask + +	/* Make sure that the NUL byte is marked in the syndrome.  */ +	orr	has_nul, has_nul, mask + +.Lnot_limit: +	orr	syndrome, diff, has_nul +	b	.Lcal_cmpresult + +.Lmutual_align: +	/* +	* Sources are mutually aligned, but are not currently at an +	* alignment boundary.  Round down the addresses and then mask off +	* the bytes that precede the start point. +	* We also need to adjust the limit calculations, but without +	* overflowing if the limit is near ULONG_MAX. +	*/ +	bic	src1, src1, #7 +	bic	src2, src2, #7 +	ldr	data1, [src1], #8 +	neg	tmp3, tmp1, lsl #3  /* 64 - bits(bytes beyond align). */ +	ldr	data2, [src2], #8 +	mov	tmp2, #~0 +	sub	limit_wd, limit, #1 /* limit != 0, so no underflow.  */ +	/* Big-endian.  Early bytes are at MSB.  */ +CPU_BE( lsl	tmp2, tmp2, tmp3 )	/* Shift (tmp1 & 63).  */ +	/* Little-endian.  Early bytes are at LSB.  */ +CPU_LE( lsr	tmp2, tmp2, tmp3 )	/* Shift (tmp1 & 63).  */ + +	and	tmp3, limit_wd, #7 +	lsr	limit_wd, limit_wd, #3 +	/* Adjust the limit. Only low 3 bits used, so overflow irrelevant.*/ +	add	limit, limit, tmp1 +	add	tmp3, tmp3, tmp1 +	orr	data1, data1, tmp2 +	orr	data2, data2, tmp2 +	add	limit_wd, limit_wd, tmp3, lsr #3 +	b	.Lstart_realigned + +/*when src1 offset is not equal to src2 offset...*/ +.Lmisaligned8: +	cmp	limit, #8 +	b.lo	.Ltiny8proc /*limit < 8... */ +	/* +	* Get the align offset length to compare per byte first. +	* After this process, one string's address will be aligned.*/ +	and	tmp1, src1, #7 +	neg	tmp1, tmp1 +	add	tmp1, tmp1, #8 +	and	tmp2, src2, #7 +	neg	tmp2, tmp2 +	add	tmp2, tmp2, #8 +	subs	tmp3, tmp1, tmp2 +	csel	pos, tmp1, tmp2, hi /*Choose the maximum. */ +	/* +	* Here, limit is not less than 8, so directly run .Ltinycmp +	* without checking the limit.*/ +	sub	limit, limit, pos +.Ltinycmp: +	ldrb	data1w, [src1], #1 +	ldrb	data2w, [src2], #1 +	subs	pos, pos, #1 +	ccmp	data1w, #1, #0, ne  /* NZCV = 0b0000.  */ +	ccmp	data1w, data2w, #0, cs  /* NZCV = 0b0000.  */ +	b.eq	.Ltinycmp +	cbnz	pos, 1f /*find the null or unequal...*/ +	cmp	data1w, #1 +	ccmp	data1w, data2w, #0, cs +	b.eq	.Lstart_align /*the last bytes are equal....*/ +1: +	sub	result, data1, data2 +	ret + +.Lstart_align: +	lsr	limit_wd, limit, #3 +	cbz	limit_wd, .Lremain8 +	/*process more leading bytes to make str1 aligned...*/ +	ands	xzr, src1, #7 +	b.eq	.Lrecal_offset +	add	src1, src1, tmp3	/*tmp3 is positive in this branch.*/ +	add	src2, src2, tmp3 +	ldr	data1, [src1], #8 +	ldr	data2, [src2], #8 + +	sub	limit, limit, tmp3 +	lsr	limit_wd, limit, #3 +	subs	limit_wd, limit_wd, #1 + +	sub	tmp1, data1, zeroones +	orr	tmp2, data1, #REP8_7f +	eor	diff, data1, data2  /* Non-zero if differences found.  */ +	csinv	endloop, diff, xzr, ne/*if limit_wd is 0,will finish the cmp*/ +	bics	has_nul, tmp1, tmp2 +	ccmp	endloop, #0, #0, eq /*has_null is ZERO: no null byte*/ +	b.ne	.Lunequal_proc +	/*How far is the current str2 from the alignment boundary...*/ +	and	tmp3, tmp3, #7 +.Lrecal_offset: +	neg	pos, tmp3 +.Lloopcmp_proc: +	/* +	* Divide the eight bytes into two parts. First,backwards the src2 +	* to an alignment boundary,load eight bytes from the SRC2 alignment +	* boundary,then compare with the relative bytes from SRC1. +	* If all 8 bytes are equal,then start the second part's comparison. +	* Otherwise finish the comparison. +	* This special handle can garantee all the accesses are in the +	* thread/task space in avoid to overrange access. +	*/ +	ldr	data1, [src1,pos] +	ldr	data2, [src2,pos] +	sub	tmp1, data1, zeroones +	orr	tmp2, data1, #REP8_7f +	bics	has_nul, tmp1, tmp2 /* Non-zero if NUL terminator.  */ +	eor	diff, data1, data2  /* Non-zero if differences found.  */ +	csinv	endloop, diff, xzr, eq +	cbnz	endloop, .Lunequal_proc + +	/*The second part process*/ +	ldr	data1, [src1], #8 +	ldr	data2, [src2], #8 +	subs	limit_wd, limit_wd, #1 +	sub	tmp1, data1, zeroones +	orr	tmp2, data1, #REP8_7f +	eor	diff, data1, data2  /* Non-zero if differences found.  */ +	csinv	endloop, diff, xzr, ne/*if limit_wd is 0,will finish the cmp*/ +	bics	has_nul, tmp1, tmp2 +	ccmp	endloop, #0, #0, eq /*has_null is ZERO: no null byte*/ +	b.eq	.Lloopcmp_proc + +.Lunequal_proc: +	orr	syndrome, diff, has_nul +	cbz	syndrome, .Lremain8 +.Lcal_cmpresult: +	/* +	* reversed the byte-order as big-endian,then CLZ can find the most +	* significant zero bits. +	*/ +CPU_LE( rev	syndrome, syndrome ) +CPU_LE( rev	data1, data1 ) +CPU_LE( rev	data2, data2 ) +	/* +	* For big-endian we cannot use the trick with the syndrome value +	* as carry-propagation can corrupt the upper bits if the trailing +	* bytes in the string contain 0x01. +	* However, if there is no NUL byte in the dword, we can generate +	* the result directly.  We can't just subtract the bytes as the +	* MSB might be significant. +	*/ +CPU_BE( cbnz	has_nul, 1f ) +CPU_BE( cmp	data1, data2 ) +CPU_BE( cset	result, ne ) +CPU_BE( cneg	result, result, lo ) +CPU_BE( ret ) +CPU_BE( 1: ) +	/* Re-compute the NUL-byte detection, using a byte-reversed value.*/ +CPU_BE( rev	tmp3, data1 ) +CPU_BE( sub	tmp1, tmp3, zeroones ) +CPU_BE( orr	tmp2, tmp3, #REP8_7f ) +CPU_BE( bic	has_nul, tmp1, tmp2 ) +CPU_BE( rev	has_nul, has_nul ) +CPU_BE( orr	syndrome, diff, has_nul ) +	/* +	* The MS-non-zero bit of the syndrome marks either the first bit +	* that is different, or the top bit of the first zero byte. +	* Shifting left now will bring the critical information into the +	* top bits. +	*/ +	clz	pos, syndrome +	lsl	data1, data1, pos +	lsl	data2, data2, pos +	/* +	* But we need to zero-extend (char is unsigned) the value and then +	* perform a signed 32-bit subtraction. +	*/ +	lsr	data1, data1, #56 +	sub	result, data1, data2, lsr #56 +	ret + +.Lremain8: +	/* Limit % 8 == 0 => all bytes significant.  */ +	ands	limit, limit, #7 +	b.eq	.Lret0 +.Ltiny8proc: +	ldrb	data1w, [src1], #1 +	ldrb	data2w, [src2], #1 +	subs	limit, limit, #1 + +	ccmp	data1w, #1, #0, ne  /* NZCV = 0b0000.  */ +	ccmp	data1w, data2w, #0, cs  /* NZCV = 0b0000.  */ +	b.eq	.Ltiny8proc +	sub	result, data1, data2 +	ret + +.Lret0: +	mov	result, #0 +	ret +ENDPROC(strncmp) diff --git a/arch/arm64/lib/strncpy_from_user.S b/arch/arm64/lib/strncpy_from_user.S deleted file mode 100644 index 56e448a831a..00000000000 --- a/arch/arm64/lib/strncpy_from_user.S +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Based on arch/arm/lib/strncpy_from_user.S - * - * Copyright (C) 1995-2000 Russell King - * Copyright (C) 2012 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 <linux/linkage.h> -#include <asm/assembler.h> -#include <asm/errno.h> - -	.text -	.align	5 - -/* - * Copy a string from user space to kernel space. - *  x0 = dst, x1 = src, x2 = byte length - * returns the number of characters copied (strlen of copied string), - *  -EFAULT on exception, or "len" if we fill the whole buffer - */ -ENTRY(__strncpy_from_user) -	mov	x4, x1 -1:	subs	x2, x2, #1 -	bmi	2f -USER(9f, ldrb	w3, [x1], #1	) -	strb	w3, [x0], #1 -	cbnz	w3, 1b -	sub	x1, x1, #1	// take NUL character out of count -2:	sub	x0, x1, x4 -	ret -ENDPROC(__strncpy_from_user) - -	.section .fixup,"ax" -	.align	0 -9:	strb	wzr, [x0]	// null terminate -	mov	x0, #-EFAULT -	ret -	.previous diff --git a/arch/arm64/lib/strnlen.S b/arch/arm64/lib/strnlen.S new file mode 100644 index 00000000000..2ca665711bf --- /dev/null +++ b/arch/arm64/lib/strnlen.S @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2013 ARM Ltd. + * Copyright (C) 2013 Linaro. + * + * This code is based on glibc cortex strings work originally authored by Linaro + * and re-licensed under GPLv2 for the Linux kernel. The original code can + * be found @ + * + * http://bazaar.launchpad.net/~linaro-toolchain-dev/cortex-strings/trunk/ + * files/head:/src/aarch64/ + * + * 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 <linux/linkage.h> +#include <asm/assembler.h> + +/* + * determine the length of a fixed-size string + * + * Parameters: + *	x0 - const string pointer + *	x1 - maximal string length + * Returns: + *	x0 - the return length of specific string + */ + +/* Arguments and results.  */ +srcin		.req	x0 +len		.req	x0 +limit		.req	x1 + +/* Locals and temporaries.  */ +src		.req	x2 +data1		.req	x3 +data2		.req	x4 +data2a		.req	x5 +has_nul1	.req	x6 +has_nul2	.req	x7 +tmp1		.req	x8 +tmp2		.req	x9 +tmp3		.req	x10 +tmp4		.req	x11 +zeroones	.req	x12 +pos		.req	x13 +limit_wd	.req	x14 + +#define REP8_01 0x0101010101010101 +#define REP8_7f 0x7f7f7f7f7f7f7f7f +#define REP8_80 0x8080808080808080 + +ENTRY(strnlen) +	cbz	limit, .Lhit_limit +	mov	zeroones, #REP8_01 +	bic	src, srcin, #15 +	ands	tmp1, srcin, #15 +	b.ne	.Lmisaligned +	/* Calculate the number of full and partial words -1.  */ +	sub	limit_wd, limit, #1 /* Limit != 0, so no underflow.  */ +	lsr	limit_wd, limit_wd, #4  /* Convert to Qwords.  */ + +	/* +	* NUL detection works on the principle that (X - 1) & (~X) & 0x80 +	* (=> (X - 1) & ~(X | 0x7f)) is non-zero iff a byte is zero, and +	* can be done in parallel across the entire word. +	*/ +	/* +	* The inner loop deals with two Dwords at a time.  This has a +	* slightly higher start-up cost, but we should win quite quickly, +	* especially on cores with a high number of issue slots per +	* cycle, as we get much better parallelism out of the operations. +	*/ +.Lloop: +	ldp	data1, data2, [src], #16 +.Lrealigned: +	sub	tmp1, data1, zeroones +	orr	tmp2, data1, #REP8_7f +	sub	tmp3, data2, zeroones +	orr	tmp4, data2, #REP8_7f +	bic	has_nul1, tmp1, tmp2 +	bic	has_nul2, tmp3, tmp4 +	subs	limit_wd, limit_wd, #1 +	orr	tmp1, has_nul1, has_nul2 +	ccmp	tmp1, #0, #0, pl    /* NZCV = 0000  */ +	b.eq	.Lloop + +	cbz	tmp1, .Lhit_limit   /* No null in final Qword.  */ + +	/* +	* We know there's a null in the final Qword. The easiest thing +	* to do now is work out the length of the string and return +	* MIN (len, limit). +	*/ +	sub	len, src, srcin +	cbz	has_nul1, .Lnul_in_data2 +CPU_BE( mov	data2, data1 )	/*perpare data to re-calculate the syndrome*/ + +	sub	len, len, #8 +	mov	has_nul2, has_nul1 +.Lnul_in_data2: +	/* +	* For big-endian, carry propagation (if the final byte in the +	* string is 0x01) means we cannot use has_nul directly.  The +	* easiest way to get the correct byte is to byte-swap the data +	* and calculate the syndrome a second time. +	*/ +CPU_BE( rev	data2, data2 ) +CPU_BE( sub	tmp1, data2, zeroones ) +CPU_BE( orr	tmp2, data2, #REP8_7f ) +CPU_BE( bic	has_nul2, tmp1, tmp2 ) + +	sub	len, len, #8 +	rev	has_nul2, has_nul2 +	clz	pos, has_nul2 +	add	len, len, pos, lsr #3       /* Bits to bytes.  */ +	cmp	len, limit +	csel	len, len, limit, ls     /* Return the lower value.  */ +	ret + +.Lmisaligned: +	/* +	* Deal with a partial first word. +	* We're doing two things in parallel here; +	* 1) Calculate the number of words (but avoiding overflow if +	* limit is near ULONG_MAX) - to do this we need to work out +	* limit + tmp1 - 1 as a 65-bit value before shifting it; +	* 2) Load and mask the initial data words - we force the bytes +	* before the ones we are interested in to 0xff - this ensures +	* early bytes will not hit any zero detection. +	*/ +	ldp	data1, data2, [src], #16 + +	sub	limit_wd, limit, #1 +	and	tmp3, limit_wd, #15 +	lsr	limit_wd, limit_wd, #4 + +	add	tmp3, tmp3, tmp1 +	add	limit_wd, limit_wd, tmp3, lsr #4 + +	neg	tmp4, tmp1 +	lsl	tmp4, tmp4, #3  /* Bytes beyond alignment -> bits.  */ + +	mov	tmp2, #~0 +	/* Big-endian.  Early bytes are at MSB.  */ +CPU_BE( lsl	tmp2, tmp2, tmp4 )	/* Shift (tmp1 & 63).  */ +	/* Little-endian.  Early bytes are at LSB.  */ +CPU_LE( lsr	tmp2, tmp2, tmp4 )	/* Shift (tmp1 & 63).  */ + +	cmp	tmp1, #8 + +	orr	data1, data1, tmp2 +	orr	data2a, data2, tmp2 + +	csinv	data1, data1, xzr, le +	csel	data2, data2, data2a, le +	b	.Lrealigned + +.Lhit_limit: +	mov	len, limit +	ret +ENDPROC(strnlen) diff --git a/arch/arm64/lib/strnlen_user.S b/arch/arm64/lib/strnlen_user.S deleted file mode 100644 index 7f7b176a564..00000000000 --- a/arch/arm64/lib/strnlen_user.S +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Based on arch/arm/lib/strnlen_user.S - * - * Copyright (C) 1995-2000 Russell King - * Copyright (C) 2012 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 <linux/linkage.h> -#include <asm/assembler.h> -#include <asm/errno.h> - -	.text -	.align	5 - -/* Prototype: unsigned long __strnlen_user(const char *str, long n) - * Purpose  : get length of a string in user memory - * Params   : str - address of string in user memory - * Returns  : length of string *including terminator* - *	      or zero on exception, or n if too long - */ -ENTRY(__strnlen_user) -	mov	x2, x0 -1:	subs	x1, x1, #1 -	b.mi	2f -USER(9f, ldrb	w3, [x0], #1	) -	cbnz	w3, 1b -2:	sub	x0, x0, x2 -	ret -ENDPROC(__strnlen_user) - -	.section .fixup,"ax" -	.align	0 -9:	mov	x0, #0 -	ret -	.previous diff --git a/arch/arm64/mm/Makefile b/arch/arm64/mm/Makefile index b51d36401d8..3ecb56c624d 100644 --- a/arch/arm64/mm/Makefile +++ b/arch/arm64/mm/Makefile @@ -1,5 +1,5 @@  obj-y				:= dma-mapping.o extable.o fault.o init.o \  				   cache.o copypage.o flush.o \  				   ioremap.o mmap.o pgd.o mmu.o \ -				   context.o tlb.o proc.o +				   context.o proc.o  obj-$(CONFIG_HUGETLB_PAGE)	+= hugetlbpage.o diff --git a/arch/arm64/mm/cache.S b/arch/arm64/mm/cache.S index 48a386094fa..23663837acf 100644 --- a/arch/arm64/mm/cache.S +++ b/arch/arm64/mm/cache.S @@ -30,8 +30,8 @@   *   *	Corrupted registers: x0-x7, x9-x11   */ -ENTRY(__flush_dcache_all) -	dsb	sy				// ensure ordering with previous memory accesses +__flush_dcache_all: +	dmb	sy				// ensure ordering with previous memory accesses  	mrs	x0, clidr_el1			// read clidr  	and	x3, x0, #0x7000000		// extract loc from clidr  	lsr	x3, x3, #23			// left align loc bit field @@ -128,7 +128,7 @@ USER(9f, dc	cvau, x4	)		// clean D line to PoU  	add	x4, x4, x2  	cmp	x4, x1  	b.lo	1b -	dsb	sy +	dsb	ish  	icache_line_size x2, x3  	sub	x3, x2, #1 @@ -139,14 +139,14 @@ USER(9f, ic	ivau, x4	)		// invalidate I line PoU  	cmp	x4, x1  	b.lo	1b  9:						// ignore any faulting cache operation -	dsb	sy +	dsb	ish  	isb  	ret  ENDPROC(flush_icache_range)  ENDPROC(__flush_cache_user_range)  /* - *	__flush_kern_dcache_page(kaddr) + *	__flush_dcache_area(kaddr, size)   *   *	Ensure that the data held in the page kaddr is written back to the   *	page in question. @@ -166,3 +166,97 @@ ENTRY(__flush_dcache_area)  	dsb	sy  	ret  ENDPROC(__flush_dcache_area) + +/* + *	__inval_cache_range(start, end) + *	- start   - start address of region + *	- end     - end address of region + */ +ENTRY(__inval_cache_range) +	/* FALLTHROUGH */ + +/* + *	__dma_inv_range(start, end) + *	- start   - virtual start address of region + *	- end     - virtual end address of region + */ +__dma_inv_range: +	dcache_line_size x2, x3 +	sub	x3, x2, #1 +	tst	x1, x3				// end cache line aligned? +	bic	x1, x1, x3 +	b.eq	1f +	dc	civac, x1			// clean & invalidate D / U line +1:	tst	x0, x3				// start cache line aligned? +	bic	x0, x0, x3 +	b.eq	2f +	dc	civac, x0			// clean & invalidate D / U line +	b	3f +2:	dc	ivac, x0			// invalidate D / U line +3:	add	x0, x0, x2 +	cmp	x0, x1 +	b.lo	2b +	dsb	sy +	ret +ENDPROC(__inval_cache_range) +ENDPROC(__dma_inv_range) + +/* + *	__dma_clean_range(start, end) + *	- start   - virtual start address of region + *	- end     - virtual end address of region + */ +__dma_clean_range: +	dcache_line_size x2, x3 +	sub	x3, x2, #1 +	bic	x0, x0, x3 +1:	dc	cvac, x0			// clean D / U line +	add	x0, x0, x2 +	cmp	x0, x1 +	b.lo	1b +	dsb	sy +	ret +ENDPROC(__dma_clean_range) + +/* + *	__dma_flush_range(start, end) + *	- start   - virtual start address of region + *	- end     - virtual end address of region + */ +ENTRY(__dma_flush_range) +	dcache_line_size x2, x3 +	sub	x3, x2, #1 +	bic	x0, x0, x3 +1:	dc	civac, x0			// clean & invalidate D / U line +	add	x0, x0, x2 +	cmp	x0, x1 +	b.lo	1b +	dsb	sy +	ret +ENDPROC(__dma_flush_range) + +/* + *	__dma_map_area(start, size, dir) + *	- start	- kernel virtual start address + *	- size	- size of region + *	- dir	- DMA direction + */ +ENTRY(__dma_map_area) +	add	x1, x1, x0 +	cmp	w2, #DMA_FROM_DEVICE +	b.eq	__dma_inv_range +	b	__dma_clean_range +ENDPROC(__dma_map_area) + +/* + *	__dma_unmap_area(start, size, dir) + *	- start	- kernel virtual start address + *	- size	- size of region + *	- dir	- DMA direction + */ +ENTRY(__dma_unmap_area) +	add	x1, x1, x0 +	cmp	w2, #DMA_TO_DEVICE +	b.ne	__dma_inv_range +	ret +ENDPROC(__dma_unmap_area) diff --git a/arch/arm64/mm/copypage.c b/arch/arm64/mm/copypage.c index 9aecbace412..13bbc3be6f5 100644 --- a/arch/arm64/mm/copypage.c +++ b/arch/arm64/mm/copypage.c @@ -27,8 +27,10 @@ void __cpu_copy_user_page(void *kto, const void *kfrom, unsigned long vaddr)  	copy_page(kto, kfrom);  	__flush_dcache_area(kto, PAGE_SIZE);  } +EXPORT_SYMBOL_GPL(__cpu_copy_user_page);  void __cpu_clear_user_page(void *kaddr, unsigned long vaddr)  {  	clear_page(kaddr);  } +EXPORT_SYMBOL_GPL(__cpu_clear_user_page); diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index 4bd7579ec9e..4164c5ace9f 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -21,34 +21,280 @@  #include <linux/export.h>  #include <linux/slab.h>  #include <linux/dma-mapping.h> +#include <linux/dma-contiguous.h> +#include <linux/of.h> +#include <linux/platform_device.h>  #include <linux/vmalloc.h>  #include <linux/swiotlb.h> +#include <linux/amba/bus.h>  #include <asm/cacheflush.h>  struct dma_map_ops *dma_ops;  EXPORT_SYMBOL(dma_ops); -static void *arm64_swiotlb_alloc_coherent(struct device *dev, size_t size, -					  dma_addr_t *dma_handle, gfp_t flags, -					  struct dma_attrs *attrs) +static pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot, +				 bool coherent)  { -	if (IS_ENABLED(CONFIG_ZONE_DMA32) && +	if (!coherent || dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs)) +		return pgprot_writecombine(prot); +	return prot; +} + +static void *__dma_alloc_coherent(struct device *dev, size_t size, +				  dma_addr_t *dma_handle, gfp_t flags, +				  struct dma_attrs *attrs) +{ +	if (dev == NULL) { +		WARN_ONCE(1, "Use an actual device structure for DMA allocation\n"); +		return NULL; +	} + +	if (IS_ENABLED(CONFIG_ZONE_DMA) &&  	    dev->coherent_dma_mask <= DMA_BIT_MASK(32)) -		flags |= GFP_DMA32; -	return swiotlb_alloc_coherent(dev, size, dma_handle, flags); +		flags |= GFP_DMA; +	if (IS_ENABLED(CONFIG_DMA_CMA)) { +		struct page *page; + +		size = PAGE_ALIGN(size); +		page = dma_alloc_from_contiguous(dev, size >> PAGE_SHIFT, +							get_order(size)); +		if (!page) +			return NULL; + +		*dma_handle = phys_to_dma(dev, page_to_phys(page)); +		return page_address(page); +	} else { +		return swiotlb_alloc_coherent(dev, size, dma_handle, flags); +	} +} + +static void __dma_free_coherent(struct device *dev, size_t size, +				void *vaddr, dma_addr_t dma_handle, +				struct dma_attrs *attrs) +{ +	if (dev == NULL) { +		WARN_ONCE(1, "Use an actual device structure for DMA allocation\n"); +		return; +	} + +	if (IS_ENABLED(CONFIG_DMA_CMA)) { +		phys_addr_t paddr = dma_to_phys(dev, dma_handle); + +		dma_release_from_contiguous(dev, +					phys_to_page(paddr), +					size >> PAGE_SHIFT); +	} else { +		swiotlb_free_coherent(dev, size, vaddr, dma_handle); +	} +} + +static void *__dma_alloc_noncoherent(struct device *dev, size_t size, +				     dma_addr_t *dma_handle, gfp_t flags, +				     struct dma_attrs *attrs) +{ +	struct page *page, **map; +	void *ptr, *coherent_ptr; +	int order, i; + +	size = PAGE_ALIGN(size); +	order = get_order(size); + +	ptr = __dma_alloc_coherent(dev, size, dma_handle, flags, attrs); +	if (!ptr) +		goto no_mem; +	map = kmalloc(sizeof(struct page *) << order, flags & ~GFP_DMA); +	if (!map) +		goto no_map; + +	/* remove any dirty cache lines on the kernel alias */ +	__dma_flush_range(ptr, ptr + size); + +	/* create a coherent mapping */ +	page = virt_to_page(ptr); +	for (i = 0; i < (size >> PAGE_SHIFT); i++) +		map[i] = page + i; +	coherent_ptr = vmap(map, size >> PAGE_SHIFT, VM_MAP, +			    __get_dma_pgprot(attrs, __pgprot(PROT_NORMAL_NC), false)); +	kfree(map); +	if (!coherent_ptr) +		goto no_map; + +	return coherent_ptr; + +no_map: +	__dma_free_coherent(dev, size, ptr, *dma_handle, attrs); +no_mem: +	*dma_handle = ~0; +	return NULL; +} + +static void __dma_free_noncoherent(struct device *dev, size_t size, +				   void *vaddr, dma_addr_t dma_handle, +				   struct dma_attrs *attrs) +{ +	void *swiotlb_addr = phys_to_virt(dma_to_phys(dev, dma_handle)); + +	vunmap(vaddr); +	__dma_free_coherent(dev, size, swiotlb_addr, dma_handle, attrs); +} + +static dma_addr_t __swiotlb_map_page(struct device *dev, struct page *page, +				     unsigned long offset, size_t size, +				     enum dma_data_direction dir, +				     struct dma_attrs *attrs) +{ +	dma_addr_t dev_addr; + +	dev_addr = swiotlb_map_page(dev, page, offset, size, dir, attrs); +	__dma_map_area(phys_to_virt(dma_to_phys(dev, dev_addr)), size, dir); + +	return dev_addr; +} + + +static void __swiotlb_unmap_page(struct device *dev, dma_addr_t dev_addr, +				 size_t size, enum dma_data_direction dir, +				 struct dma_attrs *attrs) +{ +	__dma_unmap_area(phys_to_virt(dma_to_phys(dev, dev_addr)), size, dir); +	swiotlb_unmap_page(dev, dev_addr, size, dir, attrs); +} + +static int __swiotlb_map_sg_attrs(struct device *dev, struct scatterlist *sgl, +				  int nelems, enum dma_data_direction dir, +				  struct dma_attrs *attrs) +{ +	struct scatterlist *sg; +	int i, ret; + +	ret = swiotlb_map_sg_attrs(dev, sgl, nelems, dir, attrs); +	for_each_sg(sgl, sg, ret, i) +		__dma_map_area(phys_to_virt(dma_to_phys(dev, sg->dma_address)), +			       sg->length, dir); + +	return ret; +} + +static void __swiotlb_unmap_sg_attrs(struct device *dev, +				     struct scatterlist *sgl, int nelems, +				     enum dma_data_direction dir, +				     struct dma_attrs *attrs) +{ +	struct scatterlist *sg; +	int i; + +	for_each_sg(sgl, sg, nelems, i) +		__dma_unmap_area(phys_to_virt(dma_to_phys(dev, sg->dma_address)), +				 sg->length, dir); +	swiotlb_unmap_sg_attrs(dev, sgl, nelems, dir, attrs);  } -static void arm64_swiotlb_free_coherent(struct device *dev, size_t size, -					void *vaddr, dma_addr_t dma_handle, -					struct dma_attrs *attrs) +static void __swiotlb_sync_single_for_cpu(struct device *dev, +					  dma_addr_t dev_addr, size_t size, +					  enum dma_data_direction dir)  { -	swiotlb_free_coherent(dev, size, vaddr, dma_handle); +	__dma_unmap_area(phys_to_virt(dma_to_phys(dev, dev_addr)), size, dir); +	swiotlb_sync_single_for_cpu(dev, dev_addr, size, dir);  } -static struct dma_map_ops arm64_swiotlb_dma_ops = { -	.alloc = arm64_swiotlb_alloc_coherent, -	.free = arm64_swiotlb_free_coherent, +static void __swiotlb_sync_single_for_device(struct device *dev, +					     dma_addr_t dev_addr, size_t size, +					     enum dma_data_direction dir) +{ +	swiotlb_sync_single_for_device(dev, dev_addr, size, dir); +	__dma_map_area(phys_to_virt(dma_to_phys(dev, dev_addr)), size, dir); +} + +static void __swiotlb_sync_sg_for_cpu(struct device *dev, +				      struct scatterlist *sgl, int nelems, +				      enum dma_data_direction dir) +{ +	struct scatterlist *sg; +	int i; + +	for_each_sg(sgl, sg, nelems, i) +		__dma_unmap_area(phys_to_virt(dma_to_phys(dev, sg->dma_address)), +				 sg->length, dir); +	swiotlb_sync_sg_for_cpu(dev, sgl, nelems, dir); +} + +static void __swiotlb_sync_sg_for_device(struct device *dev, +					 struct scatterlist *sgl, int nelems, +					 enum dma_data_direction dir) +{ +	struct scatterlist *sg; +	int i; + +	swiotlb_sync_sg_for_device(dev, sgl, nelems, dir); +	for_each_sg(sgl, sg, nelems, i) +		__dma_map_area(phys_to_virt(dma_to_phys(dev, sg->dma_address)), +			       sg->length, dir); +} + +/* vma->vm_page_prot must be set appropriately before calling this function */ +static int __dma_common_mmap(struct device *dev, struct vm_area_struct *vma, +			     void *cpu_addr, dma_addr_t dma_addr, size_t size) +{ +	int ret = -ENXIO; +	unsigned long nr_vma_pages = (vma->vm_end - vma->vm_start) >> +					PAGE_SHIFT; +	unsigned long nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; +	unsigned long pfn = dma_to_phys(dev, dma_addr) >> PAGE_SHIFT; +	unsigned long off = vma->vm_pgoff; + +	if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret)) +		return ret; + +	if (off < nr_pages && nr_vma_pages <= (nr_pages - off)) { +		ret = remap_pfn_range(vma, vma->vm_start, +				      pfn + off, +				      vma->vm_end - vma->vm_start, +				      vma->vm_page_prot); +	} + +	return ret; +} + +static int __swiotlb_mmap_noncoherent(struct device *dev, +		struct vm_area_struct *vma, +		void *cpu_addr, dma_addr_t dma_addr, size_t size, +		struct dma_attrs *attrs) +{ +	vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot, false); +	return __dma_common_mmap(dev, vma, cpu_addr, dma_addr, size); +} + +static int __swiotlb_mmap_coherent(struct device *dev, +		struct vm_area_struct *vma, +		void *cpu_addr, dma_addr_t dma_addr, size_t size, +		struct dma_attrs *attrs) +{ +	/* Just use whatever page_prot attributes were specified */ +	return __dma_common_mmap(dev, vma, cpu_addr, dma_addr, size); +} + +struct dma_map_ops noncoherent_swiotlb_dma_ops = { +	.alloc = __dma_alloc_noncoherent, +	.free = __dma_free_noncoherent, +	.mmap = __swiotlb_mmap_noncoherent, +	.map_page = __swiotlb_map_page, +	.unmap_page = __swiotlb_unmap_page, +	.map_sg = __swiotlb_map_sg_attrs, +	.unmap_sg = __swiotlb_unmap_sg_attrs, +	.sync_single_for_cpu = __swiotlb_sync_single_for_cpu, +	.sync_single_for_device = __swiotlb_sync_single_for_device, +	.sync_sg_for_cpu = __swiotlb_sync_sg_for_cpu, +	.sync_sg_for_device = __swiotlb_sync_sg_for_device, +	.dma_supported = swiotlb_dma_supported, +	.mapping_error = swiotlb_dma_mapping_error, +}; +EXPORT_SYMBOL(noncoherent_swiotlb_dma_ops); + +struct dma_map_ops coherent_swiotlb_dma_ops = { +	.alloc = __dma_alloc_coherent, +	.free = __dma_free_coherent, +	.mmap = __swiotlb_mmap_coherent,  	.map_page = swiotlb_map_page,  	.unmap_page = swiotlb_unmap_page,  	.map_sg = swiotlb_map_sg_attrs, @@ -60,12 +306,47 @@ static struct dma_map_ops arm64_swiotlb_dma_ops = {  	.dma_supported = swiotlb_dma_supported,  	.mapping_error = swiotlb_dma_mapping_error,  }; +EXPORT_SYMBOL(coherent_swiotlb_dma_ops); + +static int dma_bus_notifier(struct notifier_block *nb, +			    unsigned long event, void *_dev) +{ +	struct device *dev = _dev; + +	if (event != BUS_NOTIFY_ADD_DEVICE) +		return NOTIFY_DONE; + +	if (of_property_read_bool(dev->of_node, "dma-coherent")) +		set_dma_ops(dev, &coherent_swiotlb_dma_ops); + +	return NOTIFY_OK; +} + +static struct notifier_block platform_bus_nb = { +	.notifier_call = dma_bus_notifier, +}; + +static struct notifier_block amba_bus_nb = { +	.notifier_call = dma_bus_notifier, +}; + +extern int swiotlb_late_init_with_default_size(size_t default_size); -void __init arm64_swiotlb_init(void) +static int __init swiotlb_late_init(void)  { -	dma_ops = &arm64_swiotlb_dma_ops; -	swiotlb_init(1); +	size_t swiotlb_size = min(SZ_64M, MAX_ORDER_NR_PAGES << PAGE_SHIFT); + +	/* +	 * These must be registered before of_platform_populate(). +	 */ +	bus_register_notifier(&platform_bus_type, &platform_bus_nb); +	bus_register_notifier(&amba_bustype, &amba_bus_nb); + +	dma_ops = &noncoherent_swiotlb_dma_ops; + +	return swiotlb_late_init_with_default_size(swiotlb_size);  } +arch_initcall(swiotlb_late_init);  #define PREALLOC_DMA_DEBUG_ENTRIES	4096 diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index c23751b0612..bcc965e2cce 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -32,6 +32,7 @@  #include <asm/exception.h>  #include <asm/debug-monitors.h> +#include <asm/esr.h>  #include <asm/system_misc.h>  #include <asm/pgtable.h>  #include <asm/tlbflush.h> @@ -123,6 +124,7 @@ static void __do_user_fault(struct task_struct *tsk, unsigned long addr,  	}  	tsk->thread.fault_address = addr; +	tsk->thread.fault_code = esr;  	si.si_signo = sig;  	si.si_errno = 0;  	si.si_code = code; @@ -148,8 +150,6 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re  #define VM_FAULT_BADMAP		0x010000  #define VM_FAULT_BADACCESS	0x020000 -#define ESR_WRITE		(1 << 6) -#define ESR_CM			(1 << 8)  #define ESR_LNX_EXEC		(1 << 24)  static int __do_page_fault(struct mm_struct *mm, unsigned long addr, @@ -218,7 +218,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,  	if (esr & ESR_LNX_EXEC) {  		vm_flags = VM_EXEC; -	} else if ((esr & ESR_WRITE) && !(esr & ESR_CM)) { +	} else if ((esr & ESR_EL1_WRITE) && !(esr & ESR_EL1_CM)) {  		vm_flags = VM_WRITE;  		mm_flags |= FAULT_FLAG_WRITE;  	} @@ -525,7 +525,7 @@ asmlinkage int __exception do_debug_exception(unsigned long addr,  	info.si_errno = 0;  	info.si_code  = inf->code;  	info.si_addr  = (void __user *)addr; -	arm64_notify_die("", regs, &info, esr); +	arm64_notify_die("", regs, &info, 0);  	return 0;  } diff --git a/arch/arm64/mm/flush.c b/arch/arm64/mm/flush.c index e4193e3adc7..0d64089d28b 100644 --- a/arch/arm64/mm/flush.c +++ b/arch/arm64/mm/flush.c @@ -79,7 +79,8 @@ void __sync_icache_dcache(pte_t pte, unsigned long addr)  		return;  	if (!test_and_set_bit(PG_dcache_clean, &page->flags)) { -		__flush_dcache_area(page_address(page), PAGE_SIZE); +		__flush_dcache_area(page_address(page), +				PAGE_SIZE << compound_order(page));  		__flush_icache_all();  	} else if (icache_is_aivivt()) {  		__flush_icache_all(); diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c index 5e9aec35830..023747bf4dd 100644 --- a/arch/arm64/mm/hugetlbpage.c +++ b/arch/arm64/mm/hugetlbpage.c @@ -51,12 +51,11 @@ int pmd_huge(pmd_t pmd)  int pud_huge(pud_t pud)  { +#ifndef __PAGETABLE_PMD_FOLDED  	return !(pud_val(pud) & PUD_TABLE_BIT); -} - -int pmd_huge_support(void) -{ -	return 1; +#else +	return 0; +#endif  }  static __init int setup_hugepagesz(char *opt) diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c index de2de5db628..e90c5426fe1 100644 --- a/arch/arm64/mm/init.c +++ b/arch/arm64/mm/init.c @@ -30,8 +30,9 @@  #include <linux/memblock.h>  #include <linux/sort.h>  #include <linux/of_fdt.h> +#include <linux/dma-mapping.h> +#include <linux/dma-contiguous.h> -#include <asm/prom.h>  #include <asm/sections.h>  #include <asm/setup.h>  #include <asm/sizes.h> @@ -39,17 +40,9 @@  #include "mm.h" -static unsigned long phys_initrd_start __initdata = 0; -static unsigned long phys_initrd_size __initdata = 0; -  phys_addr_t memstart_addr __read_mostly = 0; -void __init early_init_dt_setup_initrd_arch(u64 start, u64 end) -{ -	phys_initrd_start = start; -	phys_initrd_size = end - start; -} - +#ifdef CONFIG_BLK_DEV_INITRD  static int __init early_initrd(char *p)  {  	unsigned long start, size; @@ -59,29 +52,39 @@ static int __init early_initrd(char *p)  	if (*endp == ',') {  		size = memparse(endp + 1, NULL); -		phys_initrd_start = start; -		phys_initrd_size = size; +		initrd_start = (unsigned long)__va(start); +		initrd_end = (unsigned long)__va(start + size);  	}  	return 0;  }  early_param("initrd", early_initrd); +#endif -#define MAX_DMA32_PFN ((4UL * 1024 * 1024 * 1024) >> PAGE_SHIFT) +/* + * Return the maximum physical address for ZONE_DMA (DMA_BIT_MASK(32)). It + * currently assumes that for memory starting above 4G, 32-bit devices will + * use a DMA offset. + */ +static phys_addr_t max_zone_dma_phys(void) +{ +	phys_addr_t offset = memblock_start_of_DRAM() & GENMASK_ULL(63, 32); +	return min(offset + (1ULL << 32), memblock_end_of_DRAM()); +}  static void __init zone_sizes_init(unsigned long min, unsigned long max)  {  	struct memblock_region *reg;  	unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES]; -	unsigned long max_dma32 = min; +	unsigned long max_dma = min;  	memset(zone_size, 0, sizeof(zone_size)); -#ifdef CONFIG_ZONE_DMA32  	/* 4GB maximum for 32-bit only capable devices */ -	max_dma32 = max(min, min(max, MAX_DMA32_PFN)); -	zone_size[ZONE_DMA32] = max_dma32 - min; -#endif -	zone_size[ZONE_NORMAL] = max - max_dma32; +	if (IS_ENABLED(CONFIG_ZONE_DMA)) { +		max_dma = PFN_DOWN(max_zone_dma_phys()); +		zone_size[ZONE_DMA] = max_dma - min; +	} +	zone_size[ZONE_NORMAL] = max - max_dma;  	memcpy(zhole_size, zone_size, sizeof(zhole_size)); @@ -91,15 +94,15 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)  		if (start >= max)  			continue; -#ifdef CONFIG_ZONE_DMA32 -		if (start < max_dma32) { -			unsigned long dma_end = min(end, max_dma32); -			zhole_size[ZONE_DMA32] -= dma_end - start; + +		if (IS_ENABLED(CONFIG_ZONE_DMA) && start < max_dma) { +			unsigned long dma_end = min(end, max_dma); +			zhole_size[ZONE_DMA] -= dma_end - start;  		} -#endif -		if (end > max_dma32) { + +		if (end > max_dma) {  			unsigned long normal_end = min(end, max); -			unsigned long normal_start = max(start, max_dma32); +			unsigned long normal_start = max(start, max_dma);  			zhole_size[ZONE_NORMAL] -= normal_end - normal_start;  		}  	} @@ -132,18 +135,13 @@ static void arm64_memory_present(void)  void __init arm64_memblock_init(void)  { -	u64 *reserve_map, base, size; +	phys_addr_t dma_phys_limit = 0;  	/* Register the kernel text, kernel data and initrd with memblock */  	memblock_reserve(__pa(_text), _end - _text);  #ifdef CONFIG_BLK_DEV_INITRD -	if (phys_initrd_size) { -		memblock_reserve(phys_initrd_start, phys_initrd_size); - -		/* Now convert initrd to virtual addresses */ -		initrd_start = __phys_to_virt(phys_initrd_start); -		initrd_end = initrd_start + phys_initrd_size; -	} +	if (initrd_start) +		memblock_reserve(__virt_to_phys(initrd_start), initrd_end - initrd_start);  #endif  	/* @@ -153,24 +151,12 @@ void __init arm64_memblock_init(void)  	memblock_reserve(__pa(swapper_pg_dir), SWAPPER_DIR_SIZE);  	memblock_reserve(__pa(idmap_pg_dir), IDMAP_DIR_SIZE); -	/* Reserve the dtb region */ -	memblock_reserve(virt_to_phys(initial_boot_params), -			 be32_to_cpu(initial_boot_params->totalsize)); +	early_init_fdt_scan_reserved_mem(); -	/* -	 * Process the reserve map.  This will probably overlap the initrd -	 * and dtb locations which are already reserved, but overlapping -	 * doesn't hurt anything -	 */ -	reserve_map = ((void*)initial_boot_params) + -			be32_to_cpu(initial_boot_params->off_mem_rsvmap); -	while (1) { -		base = be64_to_cpup(reserve_map++); -		size = be64_to_cpup(reserve_map++); -		if (!size) -			break; -		memblock_reserve(base, size); -	} +	/* 4GB maximum for 32-bit only capable devices */ +	if (IS_ENABLED(CONFIG_ZONE_DMA)) +		dma_phys_limit = max_zone_dma_phys(); +	dma_contiguous_reserve(dma_phys_limit);  	memblock_allow_resize();  	memblock_dump_all(); @@ -271,8 +257,6 @@ static void __init free_unused_memmap(void)   */  void __init mem_init(void)  { -	arm64_swiotlb_init(); -  	max_mapnr   = pfn_to_page(max_pfn + PHYS_PFN_OFFSET) - mem_map;  #ifndef CONFIG_SPARSEMEM_VMEMMAP diff --git a/arch/arm64/mm/ioremap.c b/arch/arm64/mm/ioremap.c index 1725cd6db37..7ec328392ae 100644 --- a/arch/arm64/mm/ioremap.c +++ b/arch/arm64/mm/ioremap.c @@ -25,6 +25,10 @@  #include <linux/vmalloc.h>  #include <linux/io.h> +#include <asm/fixmap.h> +#include <asm/tlbflush.h> +#include <asm/pgalloc.h> +  static void __iomem *__ioremap_caller(phys_addr_t phys_addr, size_t size,  				      pgprot_t prot, void *caller)  { @@ -77,8 +81,105 @@ EXPORT_SYMBOL(__ioremap);  void __iounmap(volatile void __iomem *io_addr)  { -	void *addr = (void *)(PAGE_MASK & (unsigned long)io_addr); +	unsigned long addr = (unsigned long)io_addr & PAGE_MASK; -	vunmap(addr); +	/* +	 * We could get an address outside vmalloc range in case +	 * of ioremap_cache() reusing a RAM mapping. +	 */ +	if (VMALLOC_START <= addr && addr < VMALLOC_END) +		vunmap((void *)addr);  }  EXPORT_SYMBOL(__iounmap); + +void __iomem *ioremap_cache(phys_addr_t phys_addr, size_t size) +{ +	/* For normal memory we already have a cacheable mapping. */ +	if (pfn_valid(__phys_to_pfn(phys_addr))) +		return (void __iomem *)__phys_to_virt(phys_addr); + +	return __ioremap_caller(phys_addr, size, __pgprot(PROT_NORMAL), +				__builtin_return_address(0)); +} +EXPORT_SYMBOL(ioremap_cache); + +#ifndef CONFIG_ARM64_64K_PAGES +static pte_t bm_pte[PTRS_PER_PTE] __page_aligned_bss; +#endif + +static inline pmd_t * __init early_ioremap_pmd(unsigned long addr) +{ +	pgd_t *pgd; +	pud_t *pud; + +	pgd = pgd_offset_k(addr); +	BUG_ON(pgd_none(*pgd) || pgd_bad(*pgd)); + +	pud = pud_offset(pgd, addr); +	BUG_ON(pud_none(*pud) || pud_bad(*pud)); + +	return pmd_offset(pud, addr); +} + +static inline pte_t * __init early_ioremap_pte(unsigned long addr) +{ +	pmd_t *pmd = early_ioremap_pmd(addr); + +	BUG_ON(pmd_none(*pmd) || pmd_bad(*pmd)); + +	return pte_offset_kernel(pmd, addr); +} + +void __init early_ioremap_init(void) +{ +	pmd_t *pmd; + +	pmd = early_ioremap_pmd(fix_to_virt(FIX_BTMAP_BEGIN)); +#ifndef CONFIG_ARM64_64K_PAGES +	/* need to populate pmd for 4k pagesize only */ +	pmd_populate_kernel(&init_mm, pmd, bm_pte); +#endif +	/* +	 * The boot-ioremap range spans multiple pmds, for which +	 * we are not prepared: +	 */ +	BUILD_BUG_ON((__fix_to_virt(FIX_BTMAP_BEGIN) >> PMD_SHIFT) +		     != (__fix_to_virt(FIX_BTMAP_END) >> PMD_SHIFT)); + +	if (pmd != early_ioremap_pmd(fix_to_virt(FIX_BTMAP_END))) { +		WARN_ON(1); +		pr_warn("pmd %p != %p\n", +			pmd, early_ioremap_pmd(fix_to_virt(FIX_BTMAP_END))); +		pr_warn("fix_to_virt(FIX_BTMAP_BEGIN): %08lx\n", +			fix_to_virt(FIX_BTMAP_BEGIN)); +		pr_warn("fix_to_virt(FIX_BTMAP_END):   %08lx\n", +			fix_to_virt(FIX_BTMAP_END)); + +		pr_warn("FIX_BTMAP_END:       %d\n", FIX_BTMAP_END); +		pr_warn("FIX_BTMAP_BEGIN:     %d\n", +			FIX_BTMAP_BEGIN); +	} + +	early_ioremap_setup(); +} + +void __init __early_set_fixmap(enum fixed_addresses idx, +			       phys_addr_t phys, pgprot_t flags) +{ +	unsigned long addr = __fix_to_virt(idx); +	pte_t *pte; + +	if (idx >= __end_of_fixed_addresses) { +		BUG(); +		return; +	} + +	pte = early_ioremap_pte(addr); + +	if (pgprot_val(flags)) +		set_pte(pte, pfn_pte(phys >> PAGE_SHIFT, flags)); +	else { +		pte_clear(&init_mm, addr, pte); +		flush_tlb_kernel_range(addr, addr+PAGE_SIZE); +	} +} diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index f557ebbe701..c43f1dd1948 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -43,11 +43,6 @@  struct page *empty_zero_page;  EXPORT_SYMBOL(empty_zero_page); -pgprot_t pgprot_default; -EXPORT_SYMBOL(pgprot_default); - -static pmdval_t prot_sect_kernel; -  struct cachepolicy {  	const char	policy[16];  	u64		mair; @@ -122,33 +117,6 @@ static int __init early_cachepolicy(char *p)  }  early_param("cachepolicy", early_cachepolicy); -/* - * Adjust the PMD section entries according to the CPU in use. - */ -static void __init init_mem_pgprot(void) -{ -	pteval_t default_pgprot; -	int i; - -	default_pgprot = PTE_ATTRINDX(MT_NORMAL); -	prot_sect_kernel = PMD_TYPE_SECT | PMD_SECT_AF | PMD_ATTRINDX(MT_NORMAL); - -#ifdef CONFIG_SMP -	/* -	 * Mark memory with the "shared" attribute for SMP systems -	 */ -	default_pgprot |= PTE_SHARED; -	prot_sect_kernel |= PMD_SECT_S; -#endif - -	for (i = 0; i < 16; i++) { -		unsigned long v = pgprot_val(protection_map[i]); -		protection_map[i] = __pgprot(v | default_pgprot); -	} - -	pgprot_default = __pgprot(PTE_TYPE_PAGE | PTE_AF | default_pgprot); -} -  pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,  			      unsigned long size, pgprot_t vma_prot)  { @@ -168,7 +136,8 @@ static void __init *early_alloc(unsigned long sz)  }  static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr, -				  unsigned long end, unsigned long pfn) +				  unsigned long end, unsigned long pfn, +				  pgprot_t prot)  {  	pte_t *pte; @@ -180,16 +149,27 @@ static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,  	pte = pte_offset_kernel(pmd, addr);  	do { -		set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC)); +		set_pte(pte, pfn_pte(pfn, prot));  		pfn++;  	} while (pte++, addr += PAGE_SIZE, addr != end);  }  static void __init alloc_init_pmd(pud_t *pud, unsigned long addr, -				  unsigned long end, phys_addr_t phys) +				  unsigned long end, phys_addr_t phys, +				  int map_io)  {  	pmd_t *pmd;  	unsigned long next; +	pmdval_t prot_sect; +	pgprot_t prot_pte; + +	if (map_io) { +		prot_sect = PROT_SECT_DEVICE_nGnRE; +		prot_pte = __pgprot(PROT_DEVICE_nGnRE); +	} else { +		prot_sect = PROT_SECT_NORMAL_EXEC; +		prot_pte = PAGE_KERNEL_EXEC; +	}  	/*  	 * Check for initial section mappings in the pgd/pud and remove them. @@ -203,23 +183,56 @@ static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,  	do {  		next = pmd_addr_end(addr, end);  		/* try section mapping first */ -		if (((addr | next | phys) & ~SECTION_MASK) == 0) -			set_pmd(pmd, __pmd(phys | prot_sect_kernel)); -		else -			alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys)); +		if (((addr | next | phys) & ~SECTION_MASK) == 0) { +			pmd_t old_pmd =*pmd; +			set_pmd(pmd, __pmd(phys | prot_sect)); +			/* +			 * Check for previous table entries created during +			 * boot (__create_page_tables) and flush them. +			 */ +			if (!pmd_none(old_pmd)) +				flush_tlb_all(); +		} else { +			alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys), +				       prot_pte); +		}  		phys += next - addr;  	} while (pmd++, addr = next, addr != end);  }  static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr, -				  unsigned long end, unsigned long phys) +				  unsigned long end, unsigned long phys, +				  int map_io)  {  	pud_t *pud = pud_offset(pgd, addr);  	unsigned long next;  	do {  		next = pud_addr_end(addr, end); -		alloc_init_pmd(pud, addr, next, phys); + +		/* +		 * For 4K granule only, attempt to put down a 1GB block +		 */ +		if (!map_io && (PAGE_SHIFT == 12) && +		    ((addr | next | phys) & ~PUD_MASK) == 0) { +			pud_t old_pud = *pud; +			set_pud(pud, __pud(phys | PROT_SECT_NORMAL_EXEC)); + +			/* +			 * If we have an old value for a pud, it will +			 * be pointing to a pmd table that we no longer +			 * need (from swapper_pg_dir). +			 * +			 * Look up the old pmd table and free it. +			 */ +			if (!pud_none(old_pud)) { +				phys_addr_t table = __pa(pmd_offset(&old_pud, 0)); +				memblock_free(table, PAGE_SIZE); +				flush_tlb_all(); +			} +		} else { +			alloc_init_pmd(pud, addr, next, phys, map_io); +		}  		phys += next - addr;  	} while (pud++, addr = next, addr != end);  } @@ -228,70 +241,43 @@ static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,   * Create the page directory entries and any necessary page tables for the   * mapping specified by 'md'.   */ -static void __init create_mapping(phys_addr_t phys, unsigned long virt, -				  phys_addr_t size) +static void __init __create_mapping(pgd_t *pgd, phys_addr_t phys, +				    unsigned long virt, phys_addr_t size, +				    int map_io)  {  	unsigned long addr, length, end, next; -	pgd_t *pgd; - -	if (virt < VMALLOC_START) { -		pr_warning("BUG: not creating mapping for 0x%016llx at 0x%016lx - outside kernel range\n", -			   phys, virt); -		return; -	}  	addr = virt & PAGE_MASK;  	length = PAGE_ALIGN(size + (virt & ~PAGE_MASK)); -	pgd = pgd_offset_k(addr);  	end = addr + length;  	do {  		next = pgd_addr_end(addr, end); -		alloc_init_pud(pgd, addr, next, phys); +		alloc_init_pud(pgd, addr, next, phys, map_io);  		phys += next - addr;  	} while (pgd++, addr = next, addr != end);  } -#ifdef CONFIG_EARLY_PRINTK -/* - * Create an early I/O mapping using the pgd/pmd entries already populated - * in head.S as this function is called too early to allocated any memory. The - * mapping size is 2MB with 4KB pages or 64KB or 64KB pages. - */ -void __iomem * __init early_io_map(phys_addr_t phys, unsigned long virt) +static void __init create_mapping(phys_addr_t phys, unsigned long virt, +				  phys_addr_t size)  { -	unsigned long size, mask; -	bool page64k = IS_ENABLED(CONFIG_ARM64_64K_PAGES); -	pgd_t *pgd; -	pud_t *pud; -	pmd_t *pmd; -	pte_t *pte; - -	/* -	 * No early pte entries with !ARM64_64K_PAGES configuration, so using -	 * sections (pmd). -	 */ -	size = page64k ? PAGE_SIZE : SECTION_SIZE; -	mask = ~(size - 1); - -	pgd = pgd_offset_k(virt); -	pud = pud_offset(pgd, virt); -	if (pud_none(*pud)) -		return NULL; -	pmd = pmd_offset(pud, virt); - -	if (page64k) { -		if (pmd_none(*pmd)) -			return NULL; -		pte = pte_offset_kernel(pmd, virt); -		set_pte(pte, __pte((phys & mask) | PROT_DEVICE_nGnRE)); -	} else { -		set_pmd(pmd, __pmd((phys & mask) | PROT_SECT_DEVICE_nGnRE)); +	if (virt < VMALLOC_START) { +		pr_warn("BUG: not creating mapping for %pa at 0x%016lx - outside kernel range\n", +			&phys, virt); +		return;  	} +	__create_mapping(pgd_offset_k(virt & PAGE_MASK), phys, virt, size, 0); +} -	return (void __iomem *)((virt & mask) + (phys & ~mask)); +void __init create_id_mapping(phys_addr_t addr, phys_addr_t size, int map_io) +{ +	if ((addr >> PGDIR_SHIFT) >= ARRAY_SIZE(idmap_pg_dir)) { +		pr_warn("BUG: not creating id mapping for %pa\n", &addr); +		return; +	} +	__create_mapping(&idmap_pg_dir[pgd_index(addr)], +			 addr, addr, size, map_io);  } -#endif  static void __init map_mem(void)  { @@ -349,7 +335,6 @@ void __init paging_init(void)  {  	void *zero_page; -	init_mem_pgprot();  	map_mem();  	/* @@ -404,10 +389,16 @@ int kern_addr_valid(unsigned long addr)  	if (pud_none(*pud))  		return 0; +	if (pud_sect(*pud)) +		return pfn_valid(pud_pfn(*pud)); +  	pmd = pmd_offset(pud, addr);  	if (pmd_none(*pmd))  		return 0; +	if (pmd_sect(*pmd)) +		return pfn_valid(pmd_pfn(*pmd)); +  	pte = pte_offset_kernel(pmd, addr);  	if (pte_none(*pte))  		return 0; @@ -448,7 +439,7 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node)  			if (!p)  				return -ENOMEM; -			set_pmd(pmd, __pmd(__pa(p) | prot_sect_kernel)); +			set_pmd(pmd, __pmd(__pa(p) | PROT_SECT_NORMAL));  		} else  			vmemmap_verify((pte_t *)pmd, node, addr, next);  	} while (addr = next, addr != end); diff --git a/arch/arm64/mm/pgd.c b/arch/arm64/mm/pgd.c index 7083cdada65..62c6101df26 100644 --- a/arch/arm64/mm/pgd.c +++ b/arch/arm64/mm/pgd.c @@ -32,17 +32,10 @@  pgd_t *pgd_alloc(struct mm_struct *mm)  { -	pgd_t *new_pgd; -  	if (PGD_SIZE == PAGE_SIZE) -		new_pgd = (pgd_t *)get_zeroed_page(GFP_KERNEL); +		return (pgd_t *)get_zeroed_page(GFP_KERNEL);  	else -		new_pgd = kzalloc(PGD_SIZE, GFP_KERNEL); - -	if (!new_pgd) -		return NULL; - -	return new_pgd; +		return kzalloc(PGD_SIZE, GFP_KERNEL);  }  void pgd_free(struct mm_struct *mm, pgd_t *pgd) diff --git a/arch/arm64/mm/proc-macros.S b/arch/arm64/mm/proc-macros.S index 8957b822010..005d29e2977 100644 --- a/arch/arm64/mm/proc-macros.S +++ b/arch/arm64/mm/proc-macros.S @@ -38,8 +38,7 @@   */  	.macro	dcache_line_size, reg, tmp  	mrs	\tmp, ctr_el0			// read CTR -	lsr	\tmp, \tmp, #16 -	and	\tmp, \tmp, #0xf		// cache line size encoding +	ubfm	\tmp, \tmp, #16, #19		// cache line size encoding  	mov	\reg, #4			// bytes per word  	lsl	\reg, \reg, \tmp		// actual cache line size  	.endm diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S index b1b31bbc967..7736779c980 100644 --- a/arch/arm64/mm/proc.S +++ b/arch/arm64/mm/proc.S @@ -28,14 +28,21 @@  #include "proc-macros.S" -#ifndef CONFIG_SMP -/* PTWs cacheable, inner/outer WBWA not shareable */ -#define TCR_FLAGS	TCR_IRGN_WBWA | TCR_ORGN_WBWA +#ifdef CONFIG_ARM64_64K_PAGES +#define TCR_TG_FLAGS	TCR_TG0_64K | TCR_TG1_64K +#else +#define TCR_TG_FLAGS	TCR_TG0_4K | TCR_TG1_4K +#endif + +#ifdef CONFIG_SMP +#define TCR_SMP_FLAGS	TCR_SHARED  #else -/* PTWs cacheable, inner/outer WBWA shareable */ -#define TCR_FLAGS	TCR_IRGN_WBWA | TCR_ORGN_WBWA | TCR_SHARED +#define TCR_SMP_FLAGS	0  #endif +/* PTWs cacheable, inner/outer WBWA */ +#define TCR_CACHE_FLAGS	TCR_IRGN_WBWA | TCR_ORGN_WBWA +  #define MAIR(attr, mt)	((attr) << ((mt) * 8))  /* @@ -80,8 +87,77 @@ ENTRY(cpu_do_idle)  	ret  ENDPROC(cpu_do_idle) +#ifdef CONFIG_ARM64_CPU_SUSPEND +/** + * cpu_do_suspend - save CPU registers context + * + * x0: virtual address of context pointer + */ +ENTRY(cpu_do_suspend) +	mrs	x2, tpidr_el0 +	mrs	x3, tpidrro_el0 +	mrs	x4, contextidr_el1 +	mrs	x5, mair_el1 +	mrs	x6, cpacr_el1 +	mrs	x7, ttbr1_el1 +	mrs	x8, tcr_el1 +	mrs	x9, vbar_el1 +	mrs	x10, mdscr_el1 +	mrs	x11, oslsr_el1 +	mrs	x12, sctlr_el1 +	stp	x2, x3, [x0] +	stp	x4, x5, [x0, #16] +	stp	x6, x7, [x0, #32] +	stp	x8, x9, [x0, #48] +	stp	x10, x11, [x0, #64] +	str	x12, [x0, #80] +	ret +ENDPROC(cpu_do_suspend) + +/** + * cpu_do_resume - restore CPU register context + * + * x0: Physical address of context pointer + * x1: ttbr0_el1 to be restored + * + * Returns: + *	sctlr_el1 value in x0 + */ +ENTRY(cpu_do_resume) +	/* +	 * Invalidate local tlb entries before turning on MMU +	 */ +	tlbi	vmalle1 +	ldp	x2, x3, [x0] +	ldp	x4, x5, [x0, #16] +	ldp	x6, x7, [x0, #32] +	ldp	x8, x9, [x0, #48] +	ldp	x10, x11, [x0, #64] +	ldr	x12, [x0, #80] +	msr	tpidr_el0, x2 +	msr	tpidrro_el0, x3 +	msr	contextidr_el1, x4 +	msr	mair_el1, x5 +	msr	cpacr_el1, x6 +	msr	ttbr0_el1, x1 +	msr	ttbr1_el1, x7 +	msr	tcr_el1, x8 +	msr	vbar_el1, x9 +	msr	mdscr_el1, x10 +	/* +	 * Restore oslsr_el1 by writing oslar_el1 +	 */ +	ubfx	x11, x11, #1, #1 +	msr	oslar_el1, x11 +	mov	x0, x12 +	dsb	nsh		// Make sure local tlb invalidation completed +	isb +	ret +ENDPROC(cpu_do_resume) +#endif +  /* - *	cpu_switch_mm(pgd_phys, tsk) + *	cpu_do_switch_mm(pgd_phys, tsk)   *   *	Set the translation table base pointer to be pgd_phys.   * @@ -104,19 +180,13 @@ ENDPROC(cpu_do_switch_mm)   *	value of the SCTLR_EL1 register.   */  ENTRY(__cpu_setup) -	/* -	 * Preserve the link register across the function call. -	 */ -	mov	x28, lr -	bl	__flush_dcache_all -	mov	lr, x28  	ic	iallu				// I+BTB cache invalidate -	dsb	sy +	tlbi	vmalle1is			// invalidate I + D TLBs +	dsb	ish  	mov	x0, #3 << 20  	msr	cpacr_el1, x0			// Enable FP/ASIMD  	msr	mdscr_el1, xzr			// Reset mdscr_el1 -	tlbi	vmalle1is			// invalidate I + D TLBs  	/*  	 * Memory region attributes for LPAE:  	 * @@ -146,12 +216,14 @@ ENTRY(__cpu_setup)  	 * Set/prepare TCR and TTBR. We use 512GB (39-bit) address range for  	 * both user and kernel.  	 */ -	ldr	x10, =TCR_TxSZ(VA_BITS) | TCR_FLAGS | TCR_IPS_40BIT | \ -		      TCR_ASID16 | TCR_TBI0 | (1 << 31) -#ifdef CONFIG_ARM64_64K_PAGES -	orr	x10, x10, TCR_TG0_64K -	orr	x10, x10, TCR_TG1_64K -#endif +	ldr	x10, =TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \ +			TCR_TG_FLAGS | TCR_ASID16 | TCR_TBI0 +	/* +	 * Read the PARange bits from ID_AA64MMFR0_EL1 and set the IPS bits in +	 * TCR_EL1. +	 */ +	mrs	x9, ID_AA64MMFR0_EL1 +	bfi	x10, x9, #32, #3  	msr	tcr_el1, x10  	ret					// return to head.S  ENDPROC(__cpu_setup) @@ -162,9 +234,9 @@ ENDPROC(__cpu_setup)  	 *       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 0001 1101 < software settings +	 * .... .1.. .... 01.1 11.1 ..01 0001 1101 < software settings  	 */  	.type	crval, #object  crval: -	.word	0x030802e2			// clear +	.word	0x000802e2			// clear  	.word	0x0405d11d			// set diff --git a/arch/arm64/mm/tlb.S b/arch/arm64/mm/tlb.S deleted file mode 100644 index 8ae80a18e8e..00000000000 --- a/arch/arm64/mm/tlb.S +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Based on arch/arm/mm/tlb.S - * - * Copyright (C) 1997-2002 Russell King - * Copyright (C) 2012 ARM Ltd. - * Written by Catalin Marinas <catalin.marinas@arm.com> - * - * 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 <linux/linkage.h> -#include <asm/assembler.h> -#include <asm/asm-offsets.h> -#include <asm/page.h> -#include <asm/tlbflush.h> -#include "proc-macros.S" - -/* - *	__cpu_flush_user_tlb_range(start, end, vma) - * - *	Invalidate a range of TLB entries in the specified address space. - * - *	- start - start address (may not be aligned) - *	- end   - end address (exclusive, may not be aligned) - *	- vma   - vma_struct describing address range - */ -ENTRY(__cpu_flush_user_tlb_range) -	vma_vm_mm x3, x2			// get vma->vm_mm -	mmid	x3, x3				// get vm_mm->context.id -	dsb	sy -	lsr	x0, x0, #12			// align address -	lsr	x1, x1, #12 -	bfi	x0, x3, #48, #16		// start VA and ASID -	bfi	x1, x3, #48, #16		// end VA and ASID -1:	tlbi	vae1is, x0			// TLB invalidate by address and ASID -	add	x0, x0, #1 -	cmp	x0, x1 -	b.lo	1b -	dsb	sy -	ret -ENDPROC(__cpu_flush_user_tlb_range) - -/* - *	__cpu_flush_kern_tlb_range(start,end) - * - *	Invalidate a range of kernel TLB entries. - * - *	- start - start address (may not be aligned) - *	- end   - end address (exclusive, may not be aligned) - */ -ENTRY(__cpu_flush_kern_tlb_range) -	dsb	sy -	lsr	x0, x0, #12			// align address -	lsr	x1, x1, #12 -1:	tlbi	vaae1is, x0			// TLB invalidate by address -	add	x0, x0, #1 -	cmp	x0, x1 -	b.lo	1b -	dsb	sy -	isb -	ret -ENDPROC(__cpu_flush_kern_tlb_range) diff --git a/arch/arm64/xen/Makefile b/arch/arm64/xen/Makefile index be240404ba9..74a8d87e542 100644 --- a/arch/arm64/xen/Makefile +++ b/arch/arm64/xen/Makefile @@ -1,2 +1,2 @@ -xen-arm-y	+= $(addprefix ../../arm/xen/, enlighten.o grant-table.o) +xen-arm-y	+= $(addprefix ../../arm/xen/, enlighten.o grant-table.o p2m.o mm.o)  obj-y		:= xen-arm.o hypercall.o diff --git a/arch/arm64/xen/hypercall.S b/arch/arm64/xen/hypercall.S index 531342ec4bc..8bbe9401f4f 100644 --- a/arch/arm64/xen/hypercall.S +++ b/arch/arm64/xen/hypercall.S @@ -80,6 +80,7 @@ HYPERCALL2(memory_op);  HYPERCALL2(physdev_op);  HYPERCALL3(vcpu_op);  HYPERCALL1(tmem_op); +HYPERCALL2(multicall);  ENTRY(privcmd_call)  	mov x16, x0  | 
