Merge "ARM: dts: msm: Add mas-crypto property to venus pil on MSM8953"
diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu
index 4987417..dfd56ec 100644
--- a/Documentation/ABI/testing/sysfs-devices-system-cpu
+++ b/Documentation/ABI/testing/sysfs-devices-system-cpu
@@ -350,3 +350,19 @@
Description: AArch64 CPU registers
'identification' directory exposes the CPU ID registers for
identifying model and revision of the CPU.
+
+What: /sys/devices/system/cpu/vulnerabilities
+ /sys/devices/system/cpu/vulnerabilities/meltdown
+ /sys/devices/system/cpu/vulnerabilities/spectre_v1
+ /sys/devices/system/cpu/vulnerabilities/spectre_v2
+Date: January 2018
+Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
+Description: Information about CPU vulnerabilities
+
+ The files are named after the code names of CPU
+ vulnerabilities. The output of those files reflects the
+ state of the CPUs in the system. Possible output values:
+
+ "Not affected" CPU is not affected by the vulnerability
+ "Vulnerable" CPU is affected and no mitigation in effect
+ "Mitigation: $M" CPU is affected and mitigation $M is in effect
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
index b0c5b57..d9d3470 100644
--- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
@@ -51,6 +51,8 @@
device (see pinctrl binding [0]).
[0]: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
+- #thermal-sensor-cells : To register ADC sensors with of_thermal. Should be 1.
+ See ./thermal.txt for a description.
Client required property:
- qcom,<consumer name>-vadc : The phandle to the corresponding vadc device.
diff --git a/Documentation/devicetree/bindings/input/qpnp-power-on.txt b/Documentation/devicetree/bindings/input/qpnp-power-on.txt
index c2550e6..33d0236 100644
--- a/Documentation/devicetree/bindings/input/qpnp-power-on.txt
+++ b/Documentation/devicetree/bindings/input/qpnp-power-on.txt
@@ -84,7 +84,34 @@
case.
- qcom,kpdpwr-sw-debounce Boolean property to enable the debounce logic
on the KPDPWR_N rising edge.
-
+- qcom,resin-pon-reset Boolean property which indicates that resin
+ needs to be configured during reset in addition
+ to the primary PON device that is configured
+ for system reset through qcom,system-reset
+ property.
+- qcom,resin-warm-reset-type Poweroff type required to be configured on
+ RESIN reset control register when the system
+ goes for warm reset. If this property is not
+ specified, then the default type, warm reset
+ will be configured to RESIN reset control
+ register. This property is effective only if
+ qcom,resin-pon-reset is defined.
+- qcom,resin-hard-reset-type Same description as qcom,resin-warm-reset-type
+ but this applies for the system hard reset case.
+- qcom,resin-shutdown-type Same description as qcom,resin-warm-reset-type
+ but this applies for the system shutdown case.
+- qcom,resin-shutdown-disable Boolean property to disable RESIN POFF
+ trigger during system shutdown case.
+ This property is effective only if
+ qcom,resin-pon-reset is defined.
+- qcom,resin-hard-reset-disable Boolean property to disable RESIN POFF
+ trigger during system hard reset case.
+ This property is effective only if
+ qcom,resin-pon-reset is defined.
+- qcom,ps-hold-shutdown-disable Boolean property to disable PS_HOLD POFF
+ trigger during system shutdown case.
+- qcom,ps-hold-hard-reset-disable Boolean property to disable PS_HOLD
+ POFF trigger during system hard reset case.
All the below properties are in the sub-node section (properties of the child
node).
diff --git a/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt b/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt
new file mode 100644
index 0000000..7dece8e
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/synaptics_dsxv26_i2c.txt
@@ -0,0 +1,50 @@
+Synaptics DSXV26 touch controller
+
+Please add this description here: The Synaptics Touch controller is connected to the
+host processor via I2C. The controller generates interrupts when the user touches
+the panel. The host controller is expected to read the touch coordinates over I2C and
+pass the coordinates to the rest of the system.
+
+Required properties:
+
+ - compatible : should be "synaptics,dsx-i2c".
+ - reg : i2c slave address of the device.
+ - interrupt-parent : parent of interrupt.
+ - synaptics,irq-gpio : irq gpio.
+ - synaptics,irq-flags : irq flags.
+
+Optional property:
+ - vdd_ana-supply : digital voltage power supply needed to power device.
+ - vcc_i2c-supply : analog voltage power supply needed to power device.
+ - synaptics,pwr-reg-name : power reg name of digital voltage.
+ - synaptics,bus-reg-name : bus reg name of analog voltage.
+ - synaptics,irq-on-state : status of irq gpio.
+ - synaptics,cap-button-codes : virtual key code mappings to be used.
+ - synaptics,vir-button-codes : virtual key code and the response region on panel.
+ - synaptics,x-flip : modify orientation of the x axis.
+ - synaptics,y-flip : modify orientation of the y axis.
+ - synaptics,reset-delay-ms : reset delay for controller (ms), default 100.
+ - synaptics,max-y-for-2d : maximal y value of the panel.
+
+Example:
+ i2c@78b7000 {
+ status = "ok";
+ synaptics@4b {
+ compatible = "synaptics,dsx-i2c";
+ reg = <0x4b>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <65 0x2008>;
+ vdd_ana-supply = <&pmtitanium_l17>;
+ vcc_i2c-supply = <&pmtitanium_l6>;
+ synaptics,pwr-reg-name = "vdd_ana";
+ synaptics,bus-reg-name = "vcc_i2c";
+ synaptics,irq-gpio = <&tlmm 65 0x2008>;
+ synaptics,irq-on-state = <0>;
+ synaptics,irq-flags = <0x2008>; /* IRQF_ONESHOT | IRQF_TRIGGER_LOW */
+ synaptics,power-delay-ms = <200>;
+ synaptics,reset-delay-ms = <200>;
+ synaptics,max-y-for-2d = <1919>; /* remove if no virtual buttons */
+ synaptics,cap-button-codes = <139 172 158>;
+ synaptics,vir-button-codes = <139 180 2000 320 160 172 540 2000 320 160 158 900 2000 320 160>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/leds/leds-qti-tri-led.txt b/Documentation/devicetree/bindings/leds/leds-qti-tri-led.txt
new file mode 100644
index 0000000..c088d42
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-qti-tri-led.txt
@@ -0,0 +1,60 @@
+Qualcomm Technologies, Inc. TRI_LED driver specific bindings
+
+This binding document describes the properties of TRI_LED module in
+Qualcomm Technologies, Inc. PMIC chips.
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Must be "qcom,tri-led".
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Register base of the TRI_LED module and length.
+
+Properties for child nodes:
+- pwms:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: The PWM device (phandle) used for controlling LED.
+
+- led-sources:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: see Documentation/devicetree/bindings/leds/common.txt;
+ Device current output identifiers are: 0 - LED1_EN,
+ 1 - LED2_EN, 2 - LED3_EN.
+
+- label:
+ Usage: optional
+ Value type: <string>
+ Definition: see Documentation/devicetree/bindings/leds/common.txt;
+
+- linux,default-trigger:
+ Usage: optional
+ Value_type: <string>
+ Definition: see Documentation/devicetree/bindings/leds/common.txt;
+
+Example:
+
+ pmi8998_rgb: tri-led@d000{
+ compatible = "qcom,tri-led";
+ reg = <0xd000 0x100>;
+
+ red {
+ label = "red";
+ pwms = <&pmi8998_lpg 4 1000000>;
+ led-sources = <0>;
+ };
+ green {
+ label = "green";
+ pwms = <&pmi8998_lpg 3 1000000>;
+ led-sources = <1>;
+ };
+ blue {
+ label = "blue";
+ pwms = <&pmi8998_lpg 2 1000000>;
+ led-sources = <2>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
index 001f74f3..6c61ada 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
@@ -121,6 +121,10 @@
- qcom,wakeup-on-idle: if configured, the mmcqd thread will call
set_wake_up_idle(), thereby voting for it to be called on idle CPUs.
+ - nvmem-cells: specifies the handle to represent the SoC revision.
+ usually it is defined by qfprom device node.
+ - nvmem-cell-names: specifies the given nvmem cell name as defined in
+ qfprom node.
Example:
diff --git a/Documentation/devicetree/bindings/pci/msm_pcie.txt b/Documentation/devicetree/bindings/pci/msm_pcie.txt
index 6af2bac..dfe5852 100644
--- a/Documentation/devicetree/bindings/pci/msm_pcie.txt
+++ b/Documentation/devicetree/bindings/pci/msm_pcie.txt
@@ -37,6 +37,7 @@
MSIs, virtual IRQ's (INT#), link state notifications.
- perst-gpio: PERST GPIO specified by PCIe spec.
- wake-gpio: WAKE GPIO specified by PCIe spec.
+ - phy-status-offset: Offset from PCIe PHY base to check if PCIe PHY is up.
- <supply-name>-supply: phandle to the regulator device tree node.
Refer to the schematics for the corresponding voltage regulators.
vreg-1.8-supply: phandle to the analog supply for the PCIe controller.
@@ -274,6 +275,7 @@
qcom,switch-latency = <100>;
qcom,wr-halt-size = <0xa>; /* 1KB */
qcom,slv-addr-space-size = <0x1000000>; /* 16MB */
+ qcom,phy-status-offset = <0x800>;
qcom,cpl-timeout = <0x2>;
iommus = <&anoc0_smmu>;
diff --git a/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
new file mode 100644
index 0000000..3174ccb
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-qti-lpg.txt
@@ -0,0 +1,36 @@
+Qualcomm Technologies, Inc. LPG driver specific bindings
+
+This binding document describes the properties of LPG (Light Pulse Generator)
+device module in Qualcomm Technologies, Inc. PMIC chips.
+
+- compatible:
+ Usage: required
+ Value type: <string>
+ Definition: Must be "qcom,pwm-lpg".
+
+- reg:
+ Usage: required
+ Value type: <prop-encoded-array>
+ Definition: Register base and length for LPG modules. The length
+ varies based on the number of channels available in
+ the PMIC chips.
+
+- reg-names:
+ Usage: required
+ Value type: <string>
+ Definition: The name of the register defined in the reg property.
+ It must be "lpg-base".
+
+- #pwm-cells:
+ Usage: required
+ Value type: <u32>
+ Definition: See Documentation/devicetree/bindings/pwm/pwm.txt;
+
+Example:
+
+ pmi8998_lpg: lpg@b100 {
+ compatible = "qcom,pwm-lpg";
+ reg = <0xb100 0x600>;
+ reg-names = "lpg-base";
+ #pwm-cells = <2>;
+ };
diff --git a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
index fba7204..be8c2f0 100644
--- a/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
+++ b/Documentation/devicetree/bindings/qdsp/msm-fastrpc.txt
@@ -14,6 +14,7 @@
- qcom,fastrpc-glink: Flag to use glink instead of smd for IPC
- qcom,rpc-latency-us: FastRPC QoS latency vote
- qcom,adsp-remoteheap-vmid: FastRPC remote heap VMID list
+- qcom,fastrpc-adsp-audio-pdr: Flag to enable ADSP Audio PDR
Optional subnodes:
- qcom,msm_fastrpc_compute_cb : Child nodes representing the compute context
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 34c2963..58c9bf8 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -293,6 +293,19 @@
- compatible : "qcom,msm-pcm-hostless"
+* audio-load-mod
+
+Required properties:
+
+ - compatible : "qcom,audio-load-mod"
+
+Optional properties:
+
+ - compatible : "qcom,audio-test-mod"
+ Add this compatible as child device to load-module device.
+ This child device is added after lpass is up to invoke
+ deferred probe devices.
+
* msm-ocmem-audio
Required properties:
@@ -355,6 +368,8 @@
- qcom,mclk-clk-reg: Indicate the register address for mclk.
+ - qcom,lpass-mclk-id: Property to update LPASS MCLK Id.
+
* audio_slimslave
Required properties:
@@ -640,6 +655,13 @@
compatible = "qcom,msm-pcm-hostless";
};
+ audio_load_mod {
+ compatible = "qcom,audio-load-mod";
+ audio_test_mod {
+ compatible = "qcom,audio-test-mod";
+ };
+ };
+
qcom,msm-ocmem-audio {
compatible = "qcom,msm-ocmem-audio";
qcom,msm_bus,name = "audio-ocmem";
diff --git a/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt b/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt
index 28ab2dd..97b71a7 100644
--- a/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt
+++ b/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt
@@ -61,6 +61,8 @@
- qcom,adc-tm-recalib-check: Add this property to check if recalibration required due to inaccuracy.
- hkadc_ldo-supply : Add this property if VADC needs to perform a Software Vote for the HKADC.
- hkadc_ok-supply : Add this property if the VADC needs to perform a Software vote for the HKADC VREG_OK.
+- #thermal-sensor-cells : To register ADC sensors with of_thermal. Should be 1.
+ See ./thermal.txt for a description.
Client required property:
- qcom,<consumer name>-adc_tm : The phandle to the corresponding adc_tm device.
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index a37e441..e996ba5 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -272,6 +272,7 @@
SUNW Sun Microsystems, Inc
swir Sierra Wireless
syna Synaptics Inc.
+synaptics Synaptics Inc.
synology Synology, Inc.
tbs TBS Technologies
tcg Trusted Computing Group
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 2b576cc..9f5bfd6 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -2707,6 +2707,11 @@
nosmt [KNL,S390] Disable symmetric multithreading (SMT).
Equivalent to smt=1.
+ nospectre_v2 [X86] Disable all mitigations for the Spectre variant 2
+ (indirect branch prediction) vulnerability. System may
+ allow data leaks with this option, which is equivalent
+ to spectre_v2=off.
+
noxsave [BUGS=X86] Disables x86 extended register state save
and restore using xsave. The kernel will fallback to
enabling legacy floating-point and sse state.
@@ -2811,6 +2816,8 @@
nopat [X86] Disable PAT (page attribute table extension of
pagetables) support.
+ nopcid [X86-64] Disable the PCID cpu feature.
+
norandmaps Don't use address space randomization. Equivalent to
echo 0 > /proc/sys/kernel/randomize_va_space
@@ -3339,6 +3346,21 @@
pt. [PARIDE]
See Documentation/blockdev/paride.txt.
+ pti= [X86_64] Control Page Table Isolation of user and
+ kernel address spaces. Disabling this feature
+ removes hardening, but improves performance of
+ system calls and interrupts.
+
+ on - unconditionally enable
+ off - unconditionally disable
+ auto - kernel detects whether your CPU model is
+ vulnerable to issues that PTI mitigates
+
+ Not specifying this option is equivalent to pti=auto.
+
+ nopti [X86_64]
+ Equivalent to pti=off
+
pty.legacy_count=
[KNL] Number of legacy pty's. Overwrites compiled-in
default number.
@@ -3943,6 +3965,29 @@
sonypi.*= [HW] Sony Programmable I/O Control Device driver
See Documentation/laptops/sonypi.txt
+ spectre_v2= [X86] Control mitigation of Spectre variant 2
+ (indirect branch speculation) vulnerability.
+
+ on - unconditionally enable
+ off - unconditionally disable
+ auto - kernel detects whether your CPU model is
+ vulnerable
+
+ Selecting 'on' will, and 'auto' may, choose a
+ mitigation method at run time according to the
+ CPU, the available microcode, the setting of the
+ CONFIG_RETPOLINE configuration option, and the
+ compiler with which the kernel was built.
+
+ Specific mitigations can also be selected manually:
+
+ retpoline - replace indirect branches
+ retpoline,generic - google's original retpoline
+ retpoline,amd - AMD-specific minimal thunk
+
+ Not specifying this option is equivalent to
+ spectre_v2=auto.
+
spia_io_base= [HW,MTD]
spia_fio_base=
spia_pedr=
diff --git a/Documentation/x86/pti.txt b/Documentation/x86/pti.txt
new file mode 100644
index 0000000..d11eff6
--- /dev/null
+++ b/Documentation/x86/pti.txt
@@ -0,0 +1,186 @@
+Overview
+========
+
+Page Table Isolation (pti, previously known as KAISER[1]) is a
+countermeasure against attacks on the shared user/kernel address
+space such as the "Meltdown" approach[2].
+
+To mitigate this class of attacks, we create an independent set of
+page tables for use only when running userspace applications. When
+the kernel is entered via syscalls, interrupts or exceptions, the
+page tables are switched to the full "kernel" copy. When the system
+switches back to user mode, the user copy is used again.
+
+The userspace page tables contain only a minimal amount of kernel
+data: only what is needed to enter/exit the kernel such as the
+entry/exit functions themselves and the interrupt descriptor table
+(IDT). There are a few strictly unnecessary things that get mapped
+such as the first C function when entering an interrupt (see
+comments in pti.c).
+
+This approach helps to ensure that side-channel attacks leveraging
+the paging structures do not function when PTI is enabled. It can be
+enabled by setting CONFIG_PAGE_TABLE_ISOLATION=y at compile time.
+Once enabled at compile-time, it can be disabled at boot with the
+'nopti' or 'pti=' kernel parameters (see kernel-parameters.txt).
+
+Page Table Management
+=====================
+
+When PTI is enabled, the kernel manages two sets of page tables.
+The first set is very similar to the single set which is present in
+kernels without PTI. This includes a complete mapping of userspace
+that the kernel can use for things like copy_to_user().
+
+Although _complete_, the user portion of the kernel page tables is
+crippled by setting the NX bit in the top level. This ensures
+that any missed kernel->user CR3 switch will immediately crash
+userspace upon executing its first instruction.
+
+The userspace page tables map only the kernel data needed to enter
+and exit the kernel. This data is entirely contained in the 'struct
+cpu_entry_area' structure which is placed in the fixmap which gives
+each CPU's copy of the area a compile-time-fixed virtual address.
+
+For new userspace mappings, the kernel makes the entries in its
+page tables like normal. The only difference is when the kernel
+makes entries in the top (PGD) level. In addition to setting the
+entry in the main kernel PGD, a copy of the entry is made in the
+userspace page tables' PGD.
+
+This sharing at the PGD level also inherently shares all the lower
+layers of the page tables. This leaves a single, shared set of
+userspace page tables to manage. One PTE to lock, one set of
+accessed bits, dirty bits, etc...
+
+Overhead
+========
+
+Protection against side-channel attacks is important. But,
+this protection comes at a cost:
+
+1. Increased Memory Use
+ a. Each process now needs an order-1 PGD instead of order-0.
+ (Consumes an additional 4k per process).
+ b. The 'cpu_entry_area' structure must be 2MB in size and 2MB
+ aligned so that it can be mapped by setting a single PMD
+ entry. This consumes nearly 2MB of RAM once the kernel
+ is decompressed, but no space in the kernel image itself.
+
+2. Runtime Cost
+ a. CR3 manipulation to switch between the page table copies
+ must be done at interrupt, syscall, and exception entry
+ and exit (it can be skipped when the kernel is interrupted,
+ though.) Moves to CR3 are on the order of a hundred
+ cycles, and are required at every entry and exit.
+ b. A "trampoline" must be used for SYSCALL entry. This
+ trampoline depends on a smaller set of resources than the
+ non-PTI SYSCALL entry code, so requires mapping fewer
+ things into the userspace page tables. The downside is
+ that stacks must be switched at entry time.
+ d. Global pages are disabled for all kernel structures not
+ mapped into both kernel and userspace page tables. This
+ feature of the MMU allows different processes to share TLB
+ entries mapping the kernel. Losing the feature means more
+ TLB misses after a context switch. The actual loss of
+ performance is very small, however, never exceeding 1%.
+ d. Process Context IDentifiers (PCID) is a CPU feature that
+ allows us to skip flushing the entire TLB when switching page
+ tables by setting a special bit in CR3 when the page tables
+ are changed. This makes switching the page tables (at context
+ switch, or kernel entry/exit) cheaper. But, on systems with
+ PCID support, the context switch code must flush both the user
+ and kernel entries out of the TLB. The user PCID TLB flush is
+ deferred until the exit to userspace, minimizing the cost.
+ See intel.com/sdm for the gory PCID/INVPCID details.
+ e. The userspace page tables must be populated for each new
+ process. Even without PTI, the shared kernel mappings
+ are created by copying top-level (PGD) entries into each
+ new process. But, with PTI, there are now *two* kernel
+ mappings: one in the kernel page tables that maps everything
+ and one for the entry/exit structures. At fork(), we need to
+ copy both.
+ f. In addition to the fork()-time copying, there must also
+ be an update to the userspace PGD any time a set_pgd() is done
+ on a PGD used to map userspace. This ensures that the kernel
+ and userspace copies always map the same userspace
+ memory.
+ g. On systems without PCID support, each CR3 write flushes
+ the entire TLB. That means that each syscall, interrupt
+ or exception flushes the TLB.
+ h. INVPCID is a TLB-flushing instruction which allows flushing
+ of TLB entries for non-current PCIDs. Some systems support
+ PCIDs, but do not support INVPCID. On these systems, addresses
+ can only be flushed from the TLB for the current PCID. When
+ flushing a kernel address, we need to flush all PCIDs, so a
+ single kernel address flush will require a TLB-flushing CR3
+ write upon the next use of every PCID.
+
+Possible Future Work
+====================
+1. We can be more careful about not actually writing to CR3
+ unless its value is actually changed.
+2. Allow PTI to be enabled/disabled at runtime in addition to the
+ boot-time switching.
+
+Testing
+========
+
+To test stability of PTI, the following test procedure is recommended,
+ideally doing all of these in parallel:
+
+1. Set CONFIG_DEBUG_ENTRY=y
+2. Run several copies of all of the tools/testing/selftests/x86/ tests
+ (excluding MPX and protection_keys) in a loop on multiple CPUs for
+ several minutes. These tests frequently uncover corner cases in the
+ kernel entry code. In general, old kernels might cause these tests
+ themselves to crash, but they should never crash the kernel.
+3. Run the 'perf' tool in a mode (top or record) that generates many
+ frequent performance monitoring non-maskable interrupts (see "NMI"
+ in /proc/interrupts). This exercises the NMI entry/exit code which
+ is known to trigger bugs in code paths that did not expect to be
+ interrupted, including nested NMIs. Using "-c" boosts the rate of
+ NMIs, and using two -c with separate counters encourages nested NMIs
+ and less deterministic behavior.
+
+ while true; do perf record -c 10000 -e instructions,cycles -a sleep 10; done
+
+4. Launch a KVM virtual machine.
+5. Run 32-bit binaries on systems supporting the SYSCALL instruction.
+ This has been a lightly-tested code path and needs extra scrutiny.
+
+Debugging
+=========
+
+Bugs in PTI cause a few different signatures of crashes
+that are worth noting here.
+
+ * Failures of the selftests/x86 code. Usually a bug in one of the
+ more obscure corners of entry_64.S
+ * Crashes in early boot, especially around CPU bringup. Bugs
+ in the trampoline code or mappings cause these.
+ * Crashes at the first interrupt. Caused by bugs in entry_64.S,
+ like screwing up a page table switch. Also caused by
+ incorrectly mapping the IRQ handler entry code.
+ * Crashes at the first NMI. The NMI code is separate from main
+ interrupt handlers and can have bugs that do not affect
+ normal interrupts. Also caused by incorrectly mapping NMI
+ code. NMIs that interrupt the entry code must be very
+ careful and can be the cause of crashes that show up when
+ running perf.
+ * Kernel crashes at the first exit to userspace. entry_64.S
+ bugs, or failing to map some of the exit code.
+ * Crashes at first interrupt that interrupts userspace. The paths
+ in entry_64.S that return to userspace are sometimes separate
+ from the ones that return to the kernel.
+ * Double faults: overflowing the kernel stack because of page
+ faults upon page faults. Caused by touching non-pti-mapped
+ data in the entry code, or forgetting to switch to kernel
+ CR3 before calling into C functions which are not pti-mapped.
+ * Userspace segfaults early in boot, sometimes manifesting
+ as mount(8) failing to mount the rootfs. These have
+ tended to be TLB invalidation issues. Usually invalidating
+ the wrong PCID, or otherwise missing an invalidation.
+
+1. https://gruss.cc/files/kaiser.pdf
+2. https://meltdownattack.com/meltdown.pdf
diff --git a/Makefile b/Makefile
index 323aa25..cf9657e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 4
PATCHLEVEL = 9
-SUBLEVEL = 73
+SUBLEVEL = 77
EXTRAVERSION =
NAME = Roaring Lionus
@@ -792,6 +792,9 @@
# disable invalid "can't wrap" optimizations for signed / pointers
KBUILD_CFLAGS += $(call cc-option,-fno-strict-overflow)
+# Make sure -fstack-check isn't enabled (like gentoo apparently did)
+KBUILD_CFLAGS += $(call cc-option,-fno-stack-check,)
+
# conserve stack if available
KBUILD_CFLAGS += $(call cc-option,-fconserve-stack)
diff --git a/arch/arc/include/asm/uaccess.h b/arch/arc/include/asm/uaccess.h
index 41faf17..0684fd2 100644
--- a/arch/arc/include/asm/uaccess.h
+++ b/arch/arc/include/asm/uaccess.h
@@ -673,6 +673,7 @@
return 0;
__asm__ __volatile__(
+ " mov lp_count, %5 \n"
" lp 3f \n"
"1: ldb.ab %3, [%2, 1] \n"
" breq.d %3, 0, 3f \n"
@@ -689,8 +690,8 @@
" .word 1b, 4b \n"
" .previous \n"
: "+r"(res), "+r"(dst), "+r"(src), "=r"(val)
- : "g"(-EFAULT), "l"(count)
- : "memory");
+ : "g"(-EFAULT), "r"(count)
+ : "lp_count", "lp_start", "lp_end", "memory");
return res;
}
diff --git a/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi b/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi
index 2106759..8f7edab 100644
--- a/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -97,6 +97,7 @@
qcom,adc-vdd-reference = <1875>;
pinctrl-names = "default";
pinctrl-0 = <&ambient_therm_default>;
+ #thermal-sensor-cells = <1>;
chan@6 {
label = "die_temp";
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie.dtsi
index 0b94534..e939bd2 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pcie.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pcie.dtsi
@@ -139,6 +139,8 @@
qcom,slv-addr-space-size = <0x40000000>;
+ qcom,phy-status-offset = <0x814>;
+
qcom,cpl-timeout = <0x2>;
qcom,boot-option = <0x1>;
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index 7d5ee77..5829942 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -17,6 +17,7 @@
#include <dt-bindings/clock/qcom,gcc-sdxpoorwills.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
+#include <dt-bindings/clock/qcom,aop-qmp.h>
/ {
model = "Qualcomm Technologies, Inc. SDX POORWILLS";
@@ -223,6 +224,13 @@
mbox-names = "apps";
};
+ clock_aop: qcom,aopclk {
+ compatible = "qcom,aop-qmp-clk-v1";
+ #clock-cells = <1>;
+ mboxes = <&qmp_aop 0>;
+ mbox-names = "qdss_clk";
+ };
+
snoc_cnoc_keepalive: qcom,snoc_cnoc_keepalive {
compatible = "qcom,devbw";
governor = "powersave";
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index b9628eb..d3500ae 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -25,6 +25,7 @@
CONFIG_PARTITION_ADVANCED=y
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_SDXPOORWILLS=y
+CONFIG_PCI_MSM=y
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_CMA=y
@@ -74,6 +75,8 @@
CONFIG_NF_CONNTRACK_TFTP=y
CONFIG_NF_CT_NETLINK=y
CONFIG_NF_CT_NETLINK_TIMEOUT=y
+CONFIG_NF_CT_NETLINK_HELPER=y
+CONFIG_NETFILTER_NETLINK_GLUE_CT=y
CONFIG_NETFILTER_XT_TARGET_LOG=y
CONFIG_NETFILTER_XT_TARGET_MARK=y
CONFIG_NETFILTER_XT_TARGET_NFLOG=y
@@ -81,6 +84,7 @@
CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
CONFIG_NETFILTER_XT_TARGET_TPROXY=y
CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
CONFIG_NETFILTER_XT_MATCH_CONNLABEL=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
@@ -104,6 +108,7 @@
CONFIG_IP_NF_TARGET_REJECT=y
CONFIG_IP_NF_NAT=y
CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NATTYPE_MODULE=y
CONFIG_IP_NF_TARGET_NETMAP=y
CONFIG_IP_NF_TARGET_REDIRECT=y
CONFIG_IP_NF_MANGLE=y
@@ -316,6 +321,7 @@
CONFIG_GPIO_USB_DETECT=y
CONFIG_USB_BAM=y
CONFIG_MSM_CLK_RPMH=y
+CONFIG_MSM_CLK_AOP_QMP=y
CONFIG_MDM_GCC_SDXPOORWILLS=y
CONFIG_MDM_CLOCK_CPU_SDXPOORWILLS=y
CONFIG_REMOTE_SPINLOCK_MSM=y
@@ -366,4 +372,13 @@
# CONFIG_DEBUG_PREEMPT is not set
CONFIG_IPC_LOGGING=y
CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_REMOTE_ETM=y
+CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_TPDA=y
+CONFIG_CORESIGHT_TPDM=y
+CONFIG_CORESIGHT_CTI=y
+CONFIG_CORESIGHT_EVENT=y
+CONFIG_CORESIGHT_HWEVENT=y
CONFIG_QMI_ENCDEC=y
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
index b5268d6..af75d1e 100644
--- a/arch/arm/configs/sdxpoorwills_defconfig
+++ b/arch/arm/configs/sdxpoorwills_defconfig
@@ -27,6 +27,7 @@
CONFIG_ARCH_QCOM=y
CONFIG_ARCH_SDXPOORWILLS=y
# CONFIG_VDSO is not set
+CONFIG_PCI_MSM=y
CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_CMA=y
@@ -76,6 +77,8 @@
CONFIG_NF_CONNTRACK_TFTP=y
CONFIG_NF_CT_NETLINK=y
CONFIG_NF_CT_NETLINK_TIMEOUT=y
+CONFIG_NF_CT_NETLINK_HELPER=y
+CONFIG_NETFILTER_NETLINK_GLUE_CT=y
CONFIG_NETFILTER_XT_TARGET_LOG=y
CONFIG_NETFILTER_XT_TARGET_MARK=y
CONFIG_NETFILTER_XT_TARGET_NFLOG=y
@@ -83,6 +86,7 @@
CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
CONFIG_NETFILTER_XT_TARGET_TPROXY=y
CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
CONFIG_NETFILTER_XT_MATCH_CONNLABEL=y
CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
@@ -106,6 +110,7 @@
CONFIG_IP_NF_TARGET_REJECT=y
CONFIG_IP_NF_NAT=y
CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NATTYPE_MODULE=y
CONFIG_IP_NF_TARGET_NETMAP=y
CONFIG_IP_NF_TARGET_REDIRECT=y
CONFIG_IP_NF_MANGLE=y
@@ -315,6 +320,7 @@
CONFIG_GPIO_USB_DETECT=y
CONFIG_USB_BAM=y
CONFIG_MSM_CLK_RPMH=y
+CONFIG_MSM_CLK_AOP_QMP=y
CONFIG_MDM_GCC_SDXPOORWILLS=y
CONFIG_MDM_CLOCK_CPU_SDXPOORWILLS=y
CONFIG_REMOTE_SPINLOCK_MSM=y
@@ -350,7 +356,6 @@
CONFIG_PWM_QPNP=y
CONFIG_QCOM_SHOW_RESUME_IRQ=y
CONFIG_ANDROID=y
-CONFIG_STM=y
CONFIG_EXT3_FS=y
CONFIG_EXT4_FS_SECURITY=y
CONFIG_VFAT_FS=y
@@ -382,6 +387,19 @@
CONFIG_IPC_LOGGING=y
CONFIG_BLK_DEV_IO_TRACE=y
CONFIG_DEBUG_USER=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
+CONFIG_CORESIGHT_REMOTE_ETM=y
+CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_QCOM_REPLICATOR=y
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_TPDA=y
+CONFIG_CORESIGHT_TPDM=y
+CONFIG_CORESIGHT_CTI=y
+CONFIG_CORESIGHT_EVENT=y
+CONFIG_CORESIGHT_TGU=y
+CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_CORESIGHT_DUMMY=y
CONFIG_CRYPTO_ECB=y
CONFIG_CRYPTO_CMAC=y
CONFIG_CRYPTO_SHA256=y
diff --git a/arch/arm/kvm/mmio.c b/arch/arm/kvm/mmio.c
index b6e715f..dac7ceb 100644
--- a/arch/arm/kvm/mmio.c
+++ b/arch/arm/kvm/mmio.c
@@ -112,7 +112,7 @@
}
trace_kvm_mmio(KVM_TRACE_MMIO_READ, len, run->mmio.phys_addr,
- data);
+ &data);
data = vcpu_data_host_to_guest(vcpu, data, len);
vcpu_set_reg(vcpu, vcpu->arch.mmio_decode.rt, data);
}
@@ -182,14 +182,14 @@
data = vcpu_data_guest_to_host(vcpu, vcpu_get_reg(vcpu, rt),
len);
- trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, fault_ipa, data);
+ trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, fault_ipa, &data);
kvm_mmio_write_buf(data_buf, len, data);
ret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, fault_ipa, len,
data_buf);
} else {
trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, len,
- fault_ipa, 0);
+ fault_ipa, NULL);
ret = kvm_io_bus_read(vcpu, KVM_MMIO_BUS, fault_ipa, len,
data_buf);
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
index f9dfe80..4952b98 100644
--- a/arch/arm/mach-qcom/Kconfig
+++ b/arch/arm/mach-qcom/Kconfig
@@ -44,6 +44,7 @@
select HAVE_ARM_ARCH_TIMER
select MSM_CORTEX_A7
select PINCTRL
+ select PCI
select QCOM_SCM if SMP
select MSM_JTAG_MM if CORESIGHT_ETM
select PM_DEVFREQ
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi
index c059443..1b38d06 100644
--- a/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -38,17 +38,13 @@
qcom,mdss-dsi-te-dcs-command = <1>;
qcom,mdss-dsi-te-check-enable;
qcom,mdss-dsi-te-using-te-pin;
- qcom,mdss-dsi-panel-timings =
- [da 34 24 00 64 68 28 38 2a 03 04 00];
- qcom,mdss-dsi-t-clk-pre = <0x29>;
- qcom,mdss-dsi-t-clk-post = <0x03>;
qcom,mdss-dsi-dma-trigger = "trigger_sw";
qcom,mdss-dsi-mdp-trigger = "none";
qcom,mdss-dsi-lp11-init;
qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
qcom,mdss-dsi-bl-min-level = <1>;
qcom,mdss-dsi-bl-max-level = <4095>;
- qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+ qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 50>;
qcom,mdss-dsi-display-timings {
timing@0 {
qcom,mdss-dsi-panel-framerate = <60>;
@@ -77,11 +73,11 @@
05 01 00 00 0a 00 02 20 00
15 01 00 00 00 00 02 bb 10
05 01 00 00 78 00 02 11 00
- 05 01 00 00 14 00 02 29 00
+ 05 01 00 00 78 00 02 29 00
];
qcom,mdss-dsi-off-command = [
- 05 01 00 00 14 00 02
- 28 00 05 01 00 00 78 00 02 10 00
+ 05 01 00 00 78 00 02 28 00
+ 05 01 00 00 78 00 02 10 00
];
qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
diff --git a/arch/arm64/boot/dts/qcom/msm8953.dtsi b/arch/arm64/boot/dts/qcom/msm8953.dtsi
index 6c6830b..69cd4fc 100644
--- a/arch/arm64/boot/dts/qcom/msm8953.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953.dtsi
@@ -675,6 +675,8 @@
reg = <0x1800000 0x80000>;
reg-names = "cc_base";
vdd_gfx-supply = <&gfx_vreg_corner>;
+ clocks = <&clock_gcc clk_xo_clk_src>;
+ clock-names = "xo";
qcom,gfxfreq-corner =
< 0 0 >,
< 133330000 1 >, /* Min SVS */
@@ -1758,6 +1760,89 @@
qcom,firmware-name = "venus";
memory-region = <&venus_mem>;
};
+
+ qcom,wcnss-wlan@0a000000 {
+ compatible = "qcom,wcnss_wlan";
+ reg = <0x0a000000 0x280000>,
+ <0x0b011008 0x04>,
+ <0x0a21b000 0x3000>,
+ <0x03204000 0x00000100>,
+ <0x03200800 0x00000200>,
+ <0x0a100400 0x00000200>,
+ <0x0a205050 0x00000200>,
+ <0x0a219000 0x00000020>,
+ <0x0a080488 0x00000008>,
+ <0x0a080fb0 0x00000008>,
+ <0x0a08040c 0x00000008>,
+ <0x0a0120a8 0x00000008>,
+ <0x0a012448 0x00000008>,
+ <0x0a080c00 0x00000001>;
+
+ reg-names = "wcnss_mmio", "wcnss_fiq",
+ "pronto_phy_base", "riva_phy_base",
+ "riva_ccu_base", "pronto_a2xb_base",
+ "pronto_ccpu_base", "pronto_saw2_base",
+ "wlan_tx_phy_aborts","wlan_brdg_err_source",
+ "wlan_tx_status", "alarms_txctl",
+ "alarms_tactl", "pronto_mcu_base";
+
+ interrupts = <0 145 0 0 146 0>;
+ interrupt-names = "wcnss_wlantx_irq", "wcnss_wlanrx_irq";
+
+ qcom,pronto-vddmx-supply = <&pm8953_s7_level_ao>;
+ qcom,pronto-vddcx-supply = <&pm8953_s2_level>;
+ qcom,pronto-vddpx-supply = <&pm8953_l5>;
+ qcom,iris-vddxo-supply = <&pm8953_l7>;
+ qcom,iris-vddrfa-supply = <&pm8953_l19>;
+ qcom,iris-vddpa-supply = <&pm8953_l9>;
+ qcom,iris-vdddig-supply = <&pm8953_l5>;
+
+ qcom,iris-vddxo-voltage-level = <1800000 0 1800000>;
+ qcom,iris-vddrfa-voltage-level = <1300000 0 1300000>;
+ qcom,iris-vddpa-voltage-level = <3300000 0 3300000>;
+ qcom,iris-vdddig-voltage-level = <1800000 0 1800000>;
+
+ qcom,vddmx-voltage-level = <RPM_SMD_REGULATOR_LEVEL_TURBO
+ RPM_SMD_REGULATOR_LEVEL_NONE
+ RPM_SMD_REGULATOR_LEVEL_TURBO>;
+ qcom,vddcx-voltage-level = <RPM_SMD_REGULATOR_LEVEL_NOM
+ RPM_SMD_REGULATOR_LEVEL_NONE
+ RPM_SMD_REGULATOR_LEVEL_TURBO>;
+ qcom,vddpx-voltage-level = <1800000 0 1800000>;
+
+ qcom,iris-vddxo-current = <10000>;
+ qcom,iris-vddrfa-current = <100000>;
+ qcom,iris-vddpa-current = <515000>;
+ qcom,iris-vdddig-current = <10000>;
+
+ qcom,pronto-vddmx-current = <0>;
+ qcom,pronto-vddcx-current = <0>;
+ qcom,pronto-vddpx-current = <0>;
+
+ pinctrl-names = "wcnss_default", "wcnss_sleep",
+ "wcnss_gpio_default";
+ pinctrl-0 = <&wcnss_default>;
+ pinctrl-1 = <&wcnss_sleep>;
+ pinctrl-2 = <&wcnss_gpio_default>;
+
+ gpios = <&tlmm 76 0>, <&tlmm 77 0>, <&tlmm 78 0>,
+ <&tlmm 79 0>, <&tlmm 80 0>;
+
+ clocks = <&clock_gcc clk_xo_wlan_clk>,
+ <&clock_gcc clk_rf_clk2>,
+ <&clock_debug clk_gcc_debug_mux>,
+ <&clock_gcc clk_wcnss_m_clk>;
+
+ clock-names = "xo", "rf_clk", "measure", "wcnss_debug";
+
+ qcom,has-autodetect-xo;
+ qcom,is-pronto-v3;
+ qcom,has-pronto-hw;
+ qcom,has-vsys-adc-channel;
+ qcom,has-a2xb-split-reg;
+ qcom,wcnss-adc_tm = <&pm8953_adc_tm>;
+ };
+
};
#include "pm8953-rpm-regulator.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/pm660.dtsi b/arch/arm64/boot/dts/qcom/pm660.dtsi
index 502b2fe..4830af4 100644
--- a/arch/arm64/boot/dts/qcom/pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm660.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -376,7 +376,7 @@
#clock-cells = <1>;
qcom,cxo-freq = <19200000>;
qcom,clkdiv-id = <1>;
- qcom,clkdiv-init-freq = <19200000>;
+ qcom,clkdiv-init-freq = <9600000>;
status = "disabled";
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
index 88a4ce7..413612d 100644
--- a/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi632.dtsi
@@ -21,3 +21,8 @@
&pm8953_typec {
status = "disabled";
};
+
+&pmi632_pon {
+ qcom,ps-hold-hard-reset-disable;
+ qcom,ps-hold-shutdown-disable;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-audio-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm670-audio-overlay.dtsi
index 5dd5c0d..2b3cb39 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-audio-overlay.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-audio-overlay.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 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
@@ -12,6 +12,7 @@
*/
#include "sdm670-wcd.dtsi"
#include "sdm670-wsa881x.dtsi"
+#include "sdm670-lpi.dtsi"
#include <dt-bindings/clock/qcom,audio-ext-clk.h>
&tavil_snd {
@@ -59,6 +60,49 @@
"SpkrLeft", "SpkrRight";
};
+&tasha_snd {
+ qcom,msm-mi2s-master = <1>, <1>, <1>, <1>, <1>;
+ qcom,audio-routing =
+ "AIF4 VI", "MCLK",
+ "RX_BIAS", "MCLK",
+ "MADINPUT", "MCLK",
+ "hifi amp", "LINEOUT1",
+ "hifi amp", "LINEOUT2",
+ "AMIC2", "MIC BIAS2",
+ "MIC BIAS2", "Headset Mic",
+ "AMIC3", "MIC BIAS2",
+ "MIC BIAS2", "ANCRight Headset Mic",
+ "AMIC4", "MIC BIAS2",
+ "MIC BIAS2", "ANCLeft Headset Mic",
+ "AMIC5", "MIC BIAS3",
+ "MIC BIAS3", "Handset Mic",
+ "DMIC0", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic0",
+ "DMIC1", "MIC BIAS1",
+ "MIC BIAS1", "Digital Mic1",
+ "DMIC2", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic2",
+ "DMIC3", "MIC BIAS3",
+ "MIC BIAS3", "Digital Mic3",
+ "DMIC4", "MIC BIAS4",
+ "MIC BIAS4", "Digital Mic4",
+ "DMIC5", "MIC BIAS4",
+ "MIC BIAS4", "Digital Mic5",
+ "SpkrLeft IN", "SPK1 OUT",
+ "SpkrRight IN", "SPK2 OUT";
+
+ qcom,msm-mbhc-hphl-swh = <0>;
+ qcom,msm-mbhc-gnd-swh = <0>;
+ qcom,msm-mclk-freq = <9600000>;
+ asoc-codec = <&stub_codec>;
+ asoc-codec-names = "msm-stub-codec.1";
+ qcom,wsa-max-devs = <2>;
+ qcom,wsa-devs = <&wsa881x_211>, <&wsa881x_212>,
+ <&wsa881x_213>, <&wsa881x_214>;
+ qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrRight",
+ "SpkrLeft", "SpkrRight";
+};
+
&int_codec {
qcom,audio-routing =
"RX_BIAS", "INT_MCLK0",
@@ -222,6 +266,31 @@
pinctrl-0 = <&wcd_intr_default>;
};
+ clock_audio_native: audio_ext_clk_native {
+ status = "disabled";
+ compatible = "qcom,audio-ref-clk";
+ #clock-cells = <1>;
+ qcom,lpass-mclk-id = <0x116>;
+ qcom,codec-mclk-clk-freq = <11289600>;
+ qcom,audio-ref-clk-gpio = <&lpi_tlmm 19 0>;
+ pinctrl-names = "sleep", "active";
+ pinctrl-0 = <&lpi_mclk0_sleep>;
+ pinctrl-1 = <&lpi_mclk0_active>;
+ };
+
+ clock_audio: audio_ext_clk {
+ status = "disabled";
+ compatible = "qcom,audio-ref-clk";
+ pinctrl-names = "active", "sleep";
+ pinctrl-0 = <&tasha_mclk_default>;
+ pinctrl-1 = <&tasha_mclk_default>;
+ qcom,audio-ref-clk-gpio = <&pm660_gpios 3 0>;
+ clock-names = "osr_clk";
+ clocks = <&pm660_div_clk>;
+ qcom,node_has_rpm_clock;
+ #clock-cells = <1>;
+ };
+
clock_audio_lnbb: audio_ext_clk_lnbb {
status = "disabled";
compatible = "qcom,audio-ref-clk";
@@ -254,6 +323,40 @@
};
&slim_aud {
+ wcd9335: tasha_codec {
+ status = "disabled";
+ compatible = "qcom,tasha-slim-pgd";
+ elemental-addr = [00 01 a0 01 17 02];
+
+ interrupt-parent = <&wcd9xxx_intc>;
+ interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+ 17 18 19 20 21 22 23 24 25 26 27 28 29
+ 30>;
+
+ qcom,wcd-rst-gpio-node = <&wcd_rst_gpio>;
+
+ clock-names = "wcd_clk", "wcd_native_clk";
+ clocks = <&clock_audio AUDIO_PMI_CLK>,
+ <&clock_audio_native AUDIO_LPASS_MCLK>;
+
+ cdc-vdd-mic-bias-supply = <&pm660l_bob>;
+ qcom,cdc-vdd-mic-bias-voltage = <3312000 3312000>;
+ qcom,cdc-vdd-mic-bias-current = <30400>;
+
+ qcom,cdc-static-supplies = "cdc-vdd-mic-bias";
+
+ qcom,cdc-micbias1-mv = <1800>;
+ qcom,cdc-micbias2-mv = <1800>;
+ qcom,cdc-micbias3-mv = <1800>;
+ qcom,cdc-micbias4-mv = <1800>;
+
+ qcom,cdc-mclk-clk-rate = <9600000>;
+ qcom,cdc-slim-ifd = "tasha-slim-ifd";
+ qcom,cdc-slim-ifd-elemental-addr = [00 00 a0 01 17 02];
+ qcom,cdc-dmic-sample-rate = <4800000>;
+ qcom,cdc-mad-dmic-rate = <600000>;
+ };
+
wcd934x_cdc: tavil_codec {
status = "disabled";
compatible = "qcom,tavil-slim-pgd";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi b/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi
index bda44cc..faaf644 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 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
@@ -12,10 +12,6 @@
*/
#include "msm-audio-lpass.dtsi"
-#include "sdm670-wcd.dtsi"
-#include "sdm670-wsa881x.dtsi"
-#include "sdm670-lpi.dtsi"
-#include <dt-bindings/clock/qcom,audio-ext-clk.h>
&msm_audio_ion {
iommus = <&apps_smmu 0x1801 0x0>;
@@ -23,6 +19,13 @@
};
&soc {
+ audio_load_mod {
+ compatible = "qcom,audio-load-mod";
+ audio_test_mod {
+ compatible = "qcom,audio-test-mod";
+ };
+ };
+
qcom,avtimer@62cf700c {
compatible = "qcom,avtimer";
reg = <0x62cf700c 0x4>,
@@ -99,6 +102,72 @@
"msm-dai-q6-tdm.36928", "msm-dai-q6-tdm.36929";
};
+ tasha_snd: sound-tasha {
+ status = "disabled";
+ compatible = "qcom,sdm670-asoc-snd-tasha";
+ qcom,model = "sdm670-tasha-snd-card";
+ qcom,wcn-btfm;
+ qcom,mi2s-audio-intf;
+ qcom,auxpcm-audio-intf;
+ asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
+ <&loopback>, <&compress>, <&hostless>,
+ <&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>,
+ <&pcm_noirq>, <&cpe3>;
+ asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
+ "msm-pcm-dsp.2", "msm-voip-dsp",
+ "msm-pcm-voice", "msm-pcm-loopback",
+ "msm-compress-dsp", "msm-pcm-hostless",
+ "msm-pcm-afe", "msm-lsm-client",
+ "msm-pcm-routing", "msm-cpe-lsm",
+ "msm-compr-dsp", "msm-pcm-dsp-noirq",
+ "msm-cpe-lsm.3";
+ asoc-cpu = <&dai_mi2s0>, <&dai_mi2s1>,
+ <&dai_mi2s2>, <&dai_mi2s3>, <&dai_mi2s4>,
+ <&dai_pri_auxpcm>, <&dai_sec_auxpcm>,
+ <&dai_tert_auxpcm>, <&dai_quat_auxpcm>,
+ <&dai_quin_auxpcm>,
+ <&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
+ <&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>,
+ <&sb_4_rx>, <&sb_4_tx>, <&sb_5_rx>, <&sb_5_tx>,
+ <&sb_6_rx>, <&sb_7_rx>, <&sb_7_tx>,
+ <&sb_8_rx>, <&sb_8_tx>,
+ <&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>,
+ <&afe_proxy_tx>, <&incall_record_rx>,
+ <&incall_record_tx>, <&incall_music_rx>,
+ <&incall_music_2_rx>,
+ <&usb_audio_rx>, <&usb_audio_tx>,
+ <&dai_pri_tdm_rx_0>, <&dai_pri_tdm_tx_0>,
+ <&dai_sec_tdm_rx_0>, <&dai_sec_tdm_tx_0>,
+ <&dai_tert_tdm_rx_0>, <&dai_tert_tdm_tx_0>,
+ <&dai_quat_tdm_rx_0>, <&dai_quat_tdm_tx_0>,
+ <&dai_quin_tdm_rx_0>, <&dai_quin_tdm_tx_0>;
+ asoc-cpu-names = "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
+ "msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
+ "msm-dai-q6-mi2s.4",
+ "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2",
+ "msm-dai-q6-auxpcm.3", "msm-dai-q6-auxpcm.4",
+ "msm-dai-q6-auxpcm.5",
+ "msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
+ "msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
+ "msm-dai-q6-dev.16388", "msm-dai-q6-dev.16389",
+ "msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
+ "msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
+ "msm-dai-q6-dev.16394", "msm-dai-q6-dev.16395",
+ "msm-dai-q6-dev.16396",
+ "msm-dai-q6-dev.16398", "msm-dai-q6-dev.16399",
+ "msm-dai-q6-dev.16400", "msm-dai-q6-dev.16401",
+ "msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
+ "msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
+ "msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
+ "msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770",
+ "msm-dai-q6-dev.28672", "msm-dai-q6-dev.28673",
+ "msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36865",
+ "msm-dai-q6-tdm.36880", "msm-dai-q6-tdm.36881",
+ "msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36897",
+ "msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36913",
+ "msm-dai-q6-tdm.36928", "msm-dai-q6-tdm.36929";
+ };
+
int_codec: sound {
status = "okay";
compatible = "qcom,sdm670-asoc-snd";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
index f287b21..75a2762 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -16,6 +16,7 @@
compatible = "qcom,pil-tz-generic";
qcom,pas-id = <13>;
qcom,firmware-name = "a615_zap";
+ memory-region = <&pil_gpu_mem>;
};
msm_bus: qcom,kgsl-busmon{
@@ -46,9 +47,12 @@
label = "kgsl-3d0";
compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d";
status = "ok";
- reg = <0x5000000 0x40000
- 0x780000 0x6300>;
- reg-names = "kgsl_3d0_reg_memory", "qfprom_memory";
+ reg = <0x5000000 0x40000>,
+ <0x5061000 0x800>,
+ <0x780000 0x6300>;
+ reg-names = "kgsl_3d0_reg_memory",
+ "kgsl_3d0_cx_dbgc_memory",
+ "qfprom_memory";
interrupts = <0 300 0>;
interrupt-names = "kgsl_3d0_irq";
qcom,id = <0>;
diff --git a/arch/arm64/boot/dts/qcom/sdm670-lpi.dtsi b/arch/arm64/boot/dts/qcom/sdm670-lpi.dtsi
index c76fbce..948c51d 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-lpi.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-lpi.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -18,6 +18,34 @@
gpio-controller;
#gpio-cells = <2>;
+ lpi_mclk0_active: lpi_mclk0_active {
+ mux {
+ pins = "gpio19";
+ function = "func1";
+ };
+
+ config {
+ pins = "gpio19";
+ drive-strength = <8>;
+ bias-disable;
+ output-low;
+ };
+ };
+
+ lpi_mclk0_sleep: lpi_mclk0_sleep {
+ mux {
+ pins = "gpio19";
+ function = "func1";
+ };
+
+ config {
+ pins = "gpio19";
+ drive-strength = <2>;
+ bias-disable;
+ bias-pull-down;
+ };
+ };
+
cdc_pdm_clk_active: cdc_pdm_clk_active {
mux {
pins = "gpio18";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
index a85060e..5684e19 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -1505,6 +1505,67 @@
};
};
+ /* Tasha WSA speaker reset pins */
+ tasha_spkr_1_sd_n {
+ tasha_spkr_1_sd_n_sleep: tasha_spkr_1_sd_n_sleep {
+ mux {
+ pins = "gpio66";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio66";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down;
+ input-enable;
+ };
+ };
+
+ tasha_spkr_1_sd_n_active: tasha_spkr_1_sd_n_active {
+ mux {
+ pins = "gpio66";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio66";
+ drive-strength = <16>; /* 16 mA */
+ bias-disable;
+ output-high;
+ };
+ };
+ };
+
+ tasha_spkr_2_sd_n {
+ tasha_spkr_2_sd_n_sleep: tasha_spkr_2_sd_n_sleep {
+ mux {
+ pins = "gpio65";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio65";
+ drive-strength = <2>; /* 2 mA */
+ bias-pull-down;
+ input-enable;
+ };
+ };
+
+ tasha_spkr_2_sd_n_active: tasha_spkr_2_sd_n_active {
+ mux {
+ pins = "gpio65";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio65";
+ drive-strength = <16>; /* 16 mA */
+ bias-disable;
+ output-high;
+ };
+ };
+ };
+
wcd_buck_vsel {
wcd_buck_vsel_default: wcd_buck_vsel_default{
mux {
@@ -1995,6 +2056,19 @@
};
};
+&pm660_gpios {
+ tasha_mclk {
+ tasha_mclk_default: tasha_mclk_default{
+ pins = "gpio3";
+ function = "func1";
+ qcom,drive-strength = <2>;
+ power-source = <0>;
+ bias-disable;
+ output-low;
+ };
+ };
+};
+
&pm660l_gpios {
camera_rear_dvdd_en {
camera_rear_dvdd_en_default: camera_rear_dvdd_en_default {
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-tasha-codec-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-tasha-codec-cdp-overlay.dts
index 15fadc6..b7cb820 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pm660a-tasha-codec-cdp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-tasha-codec-cdp-overlay.dts
@@ -21,7 +21,7 @@
#include "sdm670-cdp.dtsi"
#include "pm660a.dtsi"
-#include "sdm670-audio-overlay.dtsi"
+#include "sdm670-tasha-codec.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A + Tasha Codec CDP";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-tasha-codec-cdp.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-tasha-codec-cdp.dts
index ec906ee..1922b38 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pm660a-tasha-codec-cdp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-tasha-codec-cdp.dts
@@ -16,7 +16,7 @@
#include "sdm670.dtsi"
#include "sdm670-cdp.dtsi"
#include "pm660a.dtsi"
-#include "sdm670-audio-overlay.dtsi"
+#include "sdm670-tasha-codec.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A Tasha Codec CDP";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
index d2a6640..9a7e742 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
@@ -307,6 +307,12 @@
qcom,dsi-display-active;
};
+&dsi_panel_pwr_supply {
+ qcom,panel-supply-entry@2 {
+ qcom,supply-post-off-sleep = <5>;
+ };
+};
+
&pm660l_wled {
status = "okay";
qcom,led-strings-list = [00 01];
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
index ce88d14..007f937 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
@@ -826,8 +826,15 @@
};
&dsi_dual_nt36850_truly_cmd {
- qcom,mdss-dsi-t-clk-post = <0x0E>;
+ qcom,mdss-dsi-t-clk-post = <0x28>;
qcom,mdss-dsi-t-clk-pre = <0x30>;
+ qcom,esd-check-enabled;
+ qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+ qcom,mdss-dsi-panel-status-command = [06 01 00 01 00 00 01 0a];
+ qcom,mdss-dsi-panel-status-command-state = "dsi_hs_mode";
+ qcom,mdss-dsi-panel-status-value = <0x9c>;
+ qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+ qcom,mdss-dsi-panel-status-read-length = <1>;
qcom,mdss-dsi-display-timings {
timing@0{
qcom,mdss-dsi-panel-phy-timings = [00 1f 08 08 24 23 08
diff --git a/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-audio-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-audio-overlay.dtsi
new file mode 100644
index 0000000..80d3879
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-audio-overlay.dtsi
@@ -0,0 +1,80 @@
+/* Copyright (c) 2018, 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.
+ */
+#include "sdm670-audio-overlay.dtsi"
+
+&pmic_analog_codec {
+ status = "disabled";
+};
+
+&msm_sdw_codec {
+ status = "disabled";
+};
+
+&cdc_pdm_gpios {
+ status = "disabled";
+};
+
+&cdc_comp_gpios {
+ status = "disabled";
+};
+
+&cdc_dmic_gpios {
+ status = "disabled";
+};
+
+&cdc_sdw_gpios {
+ status = "disabled";
+};
+
+&wsa_spkr_en1 {
+ status = "disabled";
+};
+
+&wsa_spkr_en2 {
+ status = "disabled";
+};
+
+&qupv3_se8_spi {
+ status = "disabled";
+};
+
+&wcd9xxx_intc {
+ status = "okay";
+};
+
+&slim_aud {
+ status = "okay";
+};
+
+&dai_slim {
+ status = "okay";
+};
+
+&wcd9335 {
+ status = "okay";
+};
+
+&clock_audio {
+ status = "okay";
+};
+
+&clock_audio_native {
+ status = "okay";
+};
+
+&wcd_rst_gpio {
+ status = "okay";
+};
+
+&wcd9xxx_intc {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-cdp-overlay.dts
index cc772cd..af8244a 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-cdp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-cdp-overlay.dts
@@ -20,7 +20,7 @@
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include "sdm670-cdp.dtsi"
-#include "sdm670-audio-overlay.dtsi"
+#include "sdm670-tasha-codec.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L Tasha Codec CDP";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-cdp.dts b/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-cdp.dts
index e862911..55d2fc2 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-cdp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-tasha-codec-cdp.dts
@@ -15,7 +15,7 @@
#include "sdm670.dtsi"
#include "sdm670-cdp.dtsi"
-#include "sdm670-audio-overlay.dtsi"
+#include "sdm670-tasha-codec.dtsi"
/ {
model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L Tasha Codec CDP";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-tasha-codec.dtsi b/arch/arm64/boot/dts/qcom/sdm670-tasha-codec.dtsi
new file mode 100644
index 0000000..1fc0fd5
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-tasha-codec.dtsi
@@ -0,0 +1,33 @@
+/* Copyright (c) 2018, 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.
+ */
+
+#include "sdm670-tasha-codec-audio-overlay.dtsi"
+
+&int_codec {
+ status = "disabled";
+};
+
+&pm660_div_clk {
+ status = "okay";
+};
+
+&tasha_snd {
+ status = "okay";
+};
+
+&slim_aud {
+ status = "okay";
+};
+
+&dai_slim {
+ status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-usb.dtsi b/arch/arm64/boot/dts/qcom/sdm670-usb.dtsi
index 2ce829d..32864a0 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-usb.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-usb.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 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
@@ -37,6 +37,26 @@
vdd-supply = <&pm660l_l1>;
vdda18-supply = <&pm660_l10>;
vdda33-supply = <&pm660l_l7>;
+ qcom,qusb-phy-init-seq =
+ /* <value reg_offset> */
+ <0x23 0x210 /* PWR_CTRL1 */
+ 0x03 0x04 /* PLL_ANALOG_CONTROLS_TWO */
+ 0x7c 0x18c /* PLL_CLOCK_INVERTERS */
+ 0x80 0x2c /* PLL_CMODE */
+ 0x0a 0x184 /* PLL_LOCK_DELAY */
+ 0x19 0xb4 /* PLL_DIGITAL_TIMERS_TWO */
+ 0x40 0x194 /* PLL_BIAS_CONTROL_1 */
+ 0x20 0x198 /* PLL_BIAS_CONTROL_2 */
+ 0x21 0x214 /* PWR_CTRL2 */
+ 0x08 0x220 /* IMP_CTRL1 */
+ 0x58 0x224 /* IMP_CTRL2 */
+ 0x45 0x240 /* TUNE1 */
+ 0x29 0x244 /* TUNE2 */
+ 0xca 0x248 /* TUNE3 */
+ 0x04 0x24c /* TUNE4 */
+ 0x03 0x250 /* TUNE5 */
+ 0x00 0x23c /* CHG_CTRL2 */
+ 0x22 0x210>; /* PWR_CTRL1 */
};
&usb_qmp_dp_phy {
diff --git a/arch/arm64/boot/dts/qcom/sdm670-wcd.dtsi b/arch/arm64/boot/dts/qcom/sdm670-wcd.dtsi
index f8d2a04..d7120d0 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-wcd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-wcd.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -164,4 +164,20 @@
pinctrl-1 = <&hph_en1_wcd_sleep>;
};
};
+
+ tasha_codec {
+ wsa_spkr_sd1: msm_cdc_pinctrll {
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&tasha_spkr_1_sd_n_active>;
+ pinctrl-1 = <&tasha_spkr_1_sd_n_sleep>;
+ };
+
+ wsa_spkr_sd2: msm_cdc_pinctrlr {
+ compatible = "qcom,msm-cdc-pinctrl";
+ pinctrl-names = "aud_active", "aud_sleep";
+ pinctrl-0 = <&tasha_spkr_2_sd_n_active>;
+ pinctrl-1 = <&tasha_spkr_2_sd_n_sleep>;
+ };
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-wsa881x.dtsi b/arch/arm64/boot/dts/qcom/sdm670-wsa881x.dtsi
index c35850d..5dfe244 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-wsa881x.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-wsa881x.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -42,4 +42,36 @@
};
};
};
+
+ tasha_codec {
+ swr_master {
+ compatible = "qcom,swr-wcd";
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ wsa881x_211: wsa881x@20170211 {
+ compatible = "qcom,wsa881x";
+ reg = <0x0 0x20170211>;
+ qcom,spkr-sd-n-node = <&wsa_spkr_sd1>;
+ };
+
+ wsa881x_212: wsa881x@20170212 {
+ compatible = "qcom,wsa881x";
+ reg = <0x0 0x20170212>;
+ qcom,spkr-sd-n-node = <&wsa_spkr_sd2>;
+ };
+
+ wsa881x_213: wsa881x@21170213 {
+ compatible = "qcom,wsa881x";
+ reg = <0x0 0x21170213>;
+ qcom,spkr-sd-n-node = <&wsa_spkr_sd1>;
+ };
+
+ wsa881x_214: wsa881x@21170214 {
+ compatible = "qcom,wsa881x";
+ reg = <0x0 0x21170214>;
+ qcom,spkr-sd-n-node = <&wsa_spkr_sd2>;
+ };
+ };
+ };
};
diff --git a/arch/arm64/boot/dts/qcom/sdm670.dtsi b/arch/arm64/boot/dts/qcom/sdm670.dtsi
index 2b391a5..9bc083f 100644
--- a/arch/arm64/boot/dts/qcom/sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670.dtsi
@@ -529,6 +529,30 @@
reg = <0 0x93e00000 0 0x1e00000>;
};
+ pil_ipa_fw_mem: ips_fw_region@0x95c00000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0 0x95c00000 0 0x10000>;
+ };
+
+ pil_ipa_gsi_mem: ipa_gsi_region@0x95c10000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0 0x95c10000 0 0x5000>;
+ };
+
+ pil_gpu_mem: gpu_region@0x95c15000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0 0x95c15000 0 0x2000>;
+ };
+
+ qseecom_mem: qseecom_region@0x9e400000 {
+ compatible = "shared-dma-pool";
+ no-map;
+ reg = <0 0x9e400000 0 0x1400000>;
+ };
+
adsp_mem: adsp_region {
compatible = "shared-dma-pool";
alloc-ranges = <0 0x00000000 0 0xffffffff>;
@@ -537,14 +561,6 @@
size = <0 0xc00000>;
};
- qseecom_mem: qseecom_region {
- compatible = "shared-dma-pool";
- alloc-ranges = <0 0x00000000 0 0xffffffff>;
- no-map;
- alignment = <0 0x400000>;
- size = <0 0x1400000>;
- };
-
qseecom_ta_mem: qseecom_ta_region {
compatible = "shared-dma-pool";
alloc-ranges = <0 0x00000000 0 0xffffffff>;
@@ -1082,7 +1098,7 @@
vdd_pwrcl_mx_ao-supply = <&pm660l_s1_level_ao>;
qcom,mx-turbo-freq = <1440000000 1708000000 3300000001>;
- l3-devs = <&l3_cpu0 &l3_cpu6>;
+ l3-devs = <&l3_cpu0 &l3_cpu6 &l3_cdsp>;
clock-names = "xo_ao";
clocks = <&clock_rpmh RPMH_CXO_CLK_A>;
@@ -1781,6 +1797,29 @@
cell-index = <0>;
};
+ ufs_ice: ufsice@1d90000 {
+ compatible = "qcom,ice";
+ reg = <0x1d90000 0x8000>;
+ qcom,enable-ice-clk;
+ clock-names = "ufs_core_clk", "bus_clk",
+ "iface_clk", "ice_core_clk";
+ clocks = <&clock_gcc GCC_UFS_PHY_AXI_CLK>,
+ <&clock_gcc GCC_UFS_MEM_CLKREF_CLK>,
+ <&clock_gcc GCC_UFS_PHY_AHB_CLK>,
+ <&clock_gcc GCC_UFS_PHY_ICE_CORE_CLK>;
+ qcom,op-freq-hz = <0>, <0>, <0>, <300000000>;
+ vdd-hba-supply = <&ufs_phy_gdsc>;
+ qcom,msm-bus,name = "ufs_ice_noc";
+ qcom,msm-bus,num-cases = <2>;
+ qcom,msm-bus,num-paths = <1>;
+ qcom,msm-bus,vectors-KBps =
+ <1 650 0 0>, /* No vote */
+ <1 650 1000 0>; /* Max. bandwidth */
+ qcom,bus-vector-names = "MIN",
+ "MAX";
+ qcom,instance-type = "ufs";
+ };
+
ufsphy_mem: ufsphy_mem@1d87000 {
reg = <0x1d87000 0xe00>; /* PHY regs */
reg-names = "phy_mem";
@@ -1804,6 +1843,7 @@
interrupts = <0 265 0>;
phys = <&ufsphy_mem>;
phy-names = "ufsphy";
+ ufs-qcom-crypto = <&ufs_ice>;
lanes-per-direction = <1>;
dev-ref-clk-freq = <0>; /* 19.2 MHz */
@@ -1956,7 +1996,7 @@
qcom,arm-smmu;
qcom,bandwidth-vote-for-ipa;
qcom,msm-bus,name = "ipa";
- qcom,msm-bus,num-cases = <4>;
+ qcom,msm-bus,num-cases = <5>;
qcom,msm-bus,num-paths = <4>;
qcom,msm-bus,vectors-KBps =
/* No vote */
@@ -1964,22 +2004,28 @@
<90 585 0 0>,
<1 676 0 0>,
<143 777 0 0>,
+ /* SVS2 */
+ <90 512 80000 600000>,
+ <90 585 80000 350000>,
+ <1 676 40000 40000>, /*gcc_config_noc_clk_src */
+ <143 777 0 75>, /* IB defined for IPA2X_clk in MHz*/
/* SVS */
<90 512 80000 640000>,
<90 585 80000 640000>,
<1 676 80000 80000>,
- <143 777 0 150>, /* IB defined for IPA clk in MHz*/
+ <143 777 0 150>, /* IB defined for IPA2X_clk in MHz*/
/* NOMINAL */
<90 512 206000 960000>,
<90 585 206000 960000>,
<1 676 206000 160000>,
- <143 777 0 300>, /* IB defined for IPA clk in MHz*/
+ <143 777 0 300>, /* IB defined for IPA2X_clk in MHz*/
/* TURBO */
<90 512 206000 3600000>,
<90 585 206000 3600000>,
<1 676 206000 300000>,
<143 777 0 355>; /* IB defined for IPA clk in MHz*/
- qcom,bus-vector-names = "MIN", "SVS", "NOMINAL", "TURBO";
+ qcom,bus-vector-names =
+ "MIN", "SVS2", "SVS", "NOMINAL", "TURBO";
/* IPA RAM mmap */
qcom,ipa-ram-mmap = <
@@ -2319,6 +2365,8 @@
qcom,ddr-config = <0xC3040873>;
qcom,nonremovable;
+ nvmem-cells = <&minor_rev>;
+ nvmem-cell-names = "minor_rev";
status = "disabled";
};
@@ -2763,6 +2811,13 @@
< 2457600 MHZ_TO_MBPS(1804, 4) >;
};
+ l3_cdsp: qcom,l3-cdsp {
+ compatible = "devfreq-simple-dev";
+ clock-names = "devfreq_clk";
+ clocks = <&clock_cpucc L3_MISC_VOTE_CLK>;
+ governor = "powersave";
+ };
+
cpu_pmu: cpu-pmu {
compatible = "arm,armv8-pmuv3";
qcom,irq-is-percpu;
@@ -2778,6 +2833,20 @@
compatible = "syscon";
reg = <0x5091008 0x4>;
};
+
+ qfprom: qfprom@0x780000 {
+ compatible = "qcom,qfprom";
+ reg = <0x00780000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ minor_rev: minor_rev@0x78014c {
+ reg = <0x14c 0x4>;
+ bits = <0x1c 0x2>;
+ };
+ };
+
};
#include "pm660.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi b/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
index 90e1f24..a094f65 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
@@ -18,6 +18,23 @@
reg-names = "csr-base";
coresight-name = "coresight-csr";
+ qcom,usb-bam-support;
+ qcom,hwctrl-set-support;
+ qcom,set-byte-cntr-support;
+
+ qcom,blk-size = <1>;
+ };
+
+ swao_csr: csr@6b0e000 {
+ compatible = "qcom,coresight-csr";
+ reg = <0x6b0e000 0x1000>;
+ reg-names = "csr-base";
+
+ clocks = <&clock_aop QDSS_CLK>;
+ clock-names = "apb_pclk";
+
+ coresight-name = "coresight-swao-csr";
+ qcom,timestamp-support;
qcom,blk-size = <1>;
};
@@ -113,6 +130,7 @@
reg-names = "tmc-base";
coresight-name = "coresight-tmc-etf-swao";
+ coresight-csr = <&csr>;
clocks = <&clock_aop QDSS_CLK>;
clock-names = "apb_pclk";
@@ -286,6 +304,7 @@
coresight-name = "coresight-tmc-etr";
coresight-ctis = <&cti0 &cti8>;
+ coresight-csr = <&csr>;
clocks = <&clock_aop QDSS_CLK>;
clock-names = "apb_pclk";
@@ -310,6 +329,7 @@
coresight-name = "coresight-tmc-etf";
coresight-ctis = <&cti0 &cti8>;
+ coresight-csr = <&csr>;
arm,default-sink;
clocks = <&clock_aop QDSS_CLK>;
@@ -414,6 +434,7 @@
"ddr-ch23-ctrl";
coresight-name = "coresight-hwevent";
+ coresight-csr = <&csr>;
clocks = <&clock_aop QDSS_CLK>;
clock-names = "apb_pclk";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
index 33bcaa6..000f5d3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
@@ -16,6 +16,7 @@
compatible = "qcom,pil-tz-generic";
qcom,pas-id = <13>;
qcom,firmware-name = "a630_zap";
+ memory-region = <&pil_gpu_mem>;
};
msm_bus: qcom,kgsl-busmon{
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
index daf5687..af7feb5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 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
@@ -202,6 +202,8 @@
qcom,ep-latency = <10>;
+ qcom,phy-status-offset = <0x974>;
+
qcom,boot-option = <0x1>;
linux,pci-domain = <0>;
@@ -535,6 +537,8 @@
qcom,slv-addr-space-size = <0x20000000>;
+ qcom,phy-status-offset = <0x1aac>;
+
qcom,boot-option = <0x1>;
linux,pci-domain = <1>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
index 11c48bd..78be790 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
@@ -749,6 +749,123 @@
};
};
+ /* add pingrp for touchscreen */
+ pmx_ts_int_active {
+ ts_int_active: ts_int_active {
+ mux {
+ pins = "gpio122";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio122";
+ drive-strength = <8>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ pmx_ts_int_suspend {
+ ts_int_suspend1: ts_int_suspend1 {
+ mux {
+ pins = "gpio122";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio122";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+ };
+
+ pmx_ts_reset_active {
+ ts_reset_active: ts_reset_active {
+ mux {
+ pins = "gpio99";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio99";
+ drive-strength = <8>;
+ bias-pull-up;
+ };
+ };
+ };
+
+ pmx_ts_reset_suspend {
+ ts_reset_suspend1: ts_reset_suspend1 {
+ mux {
+ pins = "gpio99";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio99";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+ };
+
+ pmx_ts_release {
+ ts_release: ts_release {
+ mux {
+ pins = "gpio122", "gpio99";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio122", "gpio99";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+ };
+
+ ts_mux {
+ ts_active: ts_active {
+ mux {
+ pins = "gpio99", "gpio122";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio99", "gpio122";
+ drive-strength = <16>;
+ bias-pull-up;
+ };
+ };
+
+ ts_reset_suspend: ts_reset_suspend {
+ mux {
+ pins = "gpio99";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio99";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+
+ ts_int_suspend: ts_int_suspend {
+ mux {
+ pins = "gpio122";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio122";
+ drive-strength = <2>;
+ bias-disable;
+ };
+ };
+ };
+
sec_aux_pcm {
sec_aux_pcm_sleep: sec_aux_pcm_sleep {
mux {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
index d95dda0..a5c6ab5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
@@ -206,8 +206,8 @@
qcom,vdd-io-current-level = <200 22000>;
pinctrl-names = "active", "sleep";
- pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &storage_cd>;
- pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &storage_cd>;
+ pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &storage_cd>;
+ pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &storage_cd>;
cd-gpios = <&tlmm 126 GPIO_ACTIVE_HIGH>;
@@ -217,3 +217,30 @@
&wil6210 {
status = "ok";
};
+
+&qupv3_se5_i2c {
+ status = "ok";
+ synaptics_dsx@20 {
+ compatible = "synaptics,dsx-i2c";
+ reg = <0x20>;
+ interrupt-parent = <&tlmm>;
+ interrupts = <122 0x2008>;
+ vdd-supply = <&pm8998_l14>;
+ avdd-supply = <&pm8998_l28>;
+ pinctrl-names = "pmx_ts_active", "pmx_ts_suspend",
+ "pmx_ts_release";
+ pinctrl-0 = <&ts_int_active &ts_reset_active>;
+ pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
+ pinctrl-2 = <&ts_release>;
+ synaptics,pwr-reg-name = "avdd";
+ synaptics,bus-reg-name = "vdd";
+ synaptics,ub-i2c-addr = <0x2c>;
+ synaptics,irq-gpio = <&tlmm 122 0x2008>;
+ synaptics,reset-gpio = <&tlmm 99 0x0>;
+ synaptics,irq-on-state = <0>;
+ synaptics,power-delay-ms = <200>;
+ synaptics,reset-delay-ms = <200>;
+ synaptics,reset-on-state = <0>;
+ synaptics,reset-active-ms = <20>;
+ };
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
index 9d7c519..ec8665b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -559,7 +559,7 @@
regulator-min-microvolt = <2704000>;
regulator-max-microvolt = <2960000>;
qcom,init-voltage = <2704000>;
- qcom,init-mode = <RPMH_REGULATOR_MODE_LPM>;
+ qcom,init-mode = <RPMH_REGULATOR_MODE_HPM>;
};
};
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 213dfdb..7832165 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -2648,6 +2648,7 @@
qcom,ipa-wdi2;
qcom,use-64-bit-dma-mask;
qcom,arm-smmu;
+ qcom,smmu-fast-map;
qcom,bandwidth-vote-for-ipa;
qcom,msm-bus,name = "ipa";
qcom,msm-bus,num-cases = <5>;
@@ -2770,7 +2771,6 @@
ipa_smmu_ap: ipa_smmu_ap {
compatible = "qcom,ipa-smmu-ap-cb";
- qcom,smmu-s1-bypass;
iommus = <&apps_smmu 0x720 0x0>;
qcom,iova-mapping = <0x20000000 0x40000000>;
qcom,additional-mapping =
@@ -2780,7 +2780,6 @@
ipa_smmu_wlan: ipa_smmu_wlan {
compatible = "qcom,ipa-smmu-wlan-cb";
- qcom,smmu-s1-bypass;
iommus = <&apps_smmu 0x721 0x0>;
qcom,additional-mapping =
/* ipa-uc ram */
@@ -2789,7 +2788,6 @@
ipa_smmu_uc: ipa_smmu_uc {
compatible = "qcom,ipa-smmu-uc-cb";
- qcom,smmu-s1-bypass;
iommus = <&apps_smmu 0x722 0x0>;
qcom,iova-mapping = <0x40000000 0x20000000>;
};
@@ -3089,7 +3087,6 @@
vdd-3.3-ch0-supply = <&pm8998_l25>;
qcom,vdd-0.8-cx-mx-config = <800000 800000>;
qcom,vdd-3.3-ch0-config = <3104000 3312000>;
- qcom,smmu-s1-bypass;
};
qmi-tmd-devices {
diff --git a/arch/arm64/configs/msm8953_defconfig b/arch/arm64/configs/msm8953_defconfig
index aaceec2..009c420 100644
--- a/arch/arm64/configs/msm8953_defconfig
+++ b/arch/arm64/configs/msm8953_defconfig
@@ -514,6 +514,9 @@
CONFIG_QCOM_DCC=y
CONFIG_QTI_RPM_STATS_LOG=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_WCNSS_CORE=y
+CONFIG_WCNSS_CORE_PRONTO=y
+CONFIG_WCNSS_REGISTER_DUMP_ON_BITE=y
CONFIG_SPDM_SCM=y
CONFIG_DEVFREQ_SPDM=y
CONFIG_PWM=y
diff --git a/arch/arm64/configs/sdm670-perf_defconfig b/arch/arm64/configs/sdm670-perf_defconfig
index 1904209..8aa1e7d 100644
--- a/arch/arm64/configs/sdm670-perf_defconfig
+++ b/arch/arm64/configs/sdm670-perf_defconfig
@@ -564,6 +564,8 @@
CONFIG_ARM_GIC_V3_ACL=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_NVMEM=y
+CONFIG_QCOM_QFPROM=y
CONFIG_SENSORS_SSC=y
CONFIG_MSM_TZ_LOG=y
CONFIG_EXT2_FS=y
diff --git a/arch/arm64/configs/sdm670_defconfig b/arch/arm64/configs/sdm670_defconfig
index 0dad3f1..8022e88 100644
--- a/arch/arm64/configs/sdm670_defconfig
+++ b/arch/arm64/configs/sdm670_defconfig
@@ -582,6 +582,8 @@
CONFIG_PHY_XGENE=y
CONFIG_ANDROID=y
CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_NVMEM=y
+CONFIG_QCOM_QFPROM=y
CONFIG_SENSORS_SSC=y
CONFIG_MSM_TZ_LOG=y
CONFIG_EXT2_FS=y
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index c558bce..6e716a5 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -683,6 +683,18 @@
struct task_struct *t;
int max_users;
+ /* If nothing to change, return right away, successfully. */
+ if (value == mips_get_process_fp_mode(task))
+ return 0;
+
+ /* Only accept a mode change if 64-bit FP enabled for o32. */
+ if (!IS_ENABLED(CONFIG_MIPS_O32_FP64_SUPPORT))
+ return -EOPNOTSUPP;
+
+ /* And only for o32 tasks. */
+ if (IS_ENABLED(CONFIG_64BIT) && !test_thread_flag(TIF_32BIT_REGS))
+ return -EOPNOTSUPP;
+
/* Check the value is valid */
if (value & ~known_bits)
return -EOPNOTSUPP;
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c
index 11890e6..0c8ae2c 100644
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -439,25 +439,38 @@
#endif /* CONFIG_64BIT */
-static int fpr_get(struct task_struct *target,
- const struct user_regset *regset,
- unsigned int pos, unsigned int count,
- void *kbuf, void __user *ubuf)
+/*
+ * Copy the floating-point context to the supplied NT_PRFPREG buffer,
+ * !CONFIG_CPU_HAS_MSA variant. FP context's general register slots
+ * correspond 1:1 to buffer slots. Only general registers are copied.
+ */
+static int fpr_get_fpa(struct task_struct *target,
+ unsigned int *pos, unsigned int *count,
+ void **kbuf, void __user **ubuf)
{
- unsigned i;
- int err;
+ return user_regset_copyout(pos, count, kbuf, ubuf,
+ &target->thread.fpu,
+ 0, NUM_FPU_REGS * sizeof(elf_fpreg_t));
+}
+
+/*
+ * Copy the floating-point context to the supplied NT_PRFPREG buffer,
+ * CONFIG_CPU_HAS_MSA variant. Only lower 64 bits of FP context's
+ * general register slots are copied to buffer slots. Only general
+ * registers are copied.
+ */
+static int fpr_get_msa(struct task_struct *target,
+ unsigned int *pos, unsigned int *count,
+ void **kbuf, void __user **ubuf)
+{
+ unsigned int i;
u64 fpr_val;
+ int err;
- /* XXX fcr31 */
-
- if (sizeof(target->thread.fpu.fpr[i]) == sizeof(elf_fpreg_t))
- return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
- &target->thread.fpu,
- 0, sizeof(elf_fpregset_t));
-
+ BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t));
for (i = 0; i < NUM_FPU_REGS; i++) {
fpr_val = get_fpr64(&target->thread.fpu.fpr[i], 0);
- err = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ err = user_regset_copyout(pos, count, kbuf, ubuf,
&fpr_val, i * sizeof(elf_fpreg_t),
(i + 1) * sizeof(elf_fpreg_t));
if (err)
@@ -467,27 +480,64 @@
return 0;
}
-static int fpr_set(struct task_struct *target,
+/*
+ * Copy the floating-point context to the supplied NT_PRFPREG buffer.
+ * Choose the appropriate helper for general registers, and then copy
+ * the FCSR register separately.
+ */
+static int fpr_get(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
- const void *kbuf, const void __user *ubuf)
+ void *kbuf, void __user *ubuf)
{
- unsigned i;
+ const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t);
int err;
+
+ if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t))
+ err = fpr_get_fpa(target, &pos, &count, &kbuf, &ubuf);
+ else
+ err = fpr_get_msa(target, &pos, &count, &kbuf, &ubuf);
+ if (err)
+ return err;
+
+ err = user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.fpu.fcr31,
+ fcr31_pos, fcr31_pos + sizeof(u32));
+
+ return err;
+}
+
+/*
+ * Copy the supplied NT_PRFPREG buffer to the floating-point context,
+ * !CONFIG_CPU_HAS_MSA variant. Buffer slots correspond 1:1 to FP
+ * context's general register slots. Only general registers are copied.
+ */
+static int fpr_set_fpa(struct task_struct *target,
+ unsigned int *pos, unsigned int *count,
+ const void **kbuf, const void __user **ubuf)
+{
+ return user_regset_copyin(pos, count, kbuf, ubuf,
+ &target->thread.fpu,
+ 0, NUM_FPU_REGS * sizeof(elf_fpreg_t));
+}
+
+/*
+ * Copy the supplied NT_PRFPREG buffer to the floating-point context,
+ * CONFIG_CPU_HAS_MSA variant. Buffer slots are copied to lower 64
+ * bits only of FP context's general register slots. Only general
+ * registers are copied.
+ */
+static int fpr_set_msa(struct task_struct *target,
+ unsigned int *pos, unsigned int *count,
+ const void **kbuf, const void __user **ubuf)
+{
+ unsigned int i;
u64 fpr_val;
-
- /* XXX fcr31 */
-
- init_fp_ctx(target);
-
- if (sizeof(target->thread.fpu.fpr[i]) == sizeof(elf_fpreg_t))
- return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
- &target->thread.fpu,
- 0, sizeof(elf_fpregset_t));
+ int err;
BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t));
- for (i = 0; i < NUM_FPU_REGS && count >= sizeof(elf_fpreg_t); i++) {
- err = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ for (i = 0; i < NUM_FPU_REGS && *count > 0; i++) {
+ err = user_regset_copyin(pos, count, kbuf, ubuf,
&fpr_val, i * sizeof(elf_fpreg_t),
(i + 1) * sizeof(elf_fpreg_t));
if (err)
@@ -498,6 +548,53 @@
return 0;
}
+/*
+ * Copy the supplied NT_PRFPREG buffer to the floating-point context.
+ * Choose the appropriate helper for general registers, and then copy
+ * the FCSR register separately.
+ *
+ * We optimize for the case where `count % sizeof(elf_fpreg_t) == 0',
+ * which is supposed to have been guaranteed by the kernel before
+ * calling us, e.g. in `ptrace_regset'. We enforce that requirement,
+ * so that we can safely avoid preinitializing temporaries for
+ * partial register writes.
+ */
+static int fpr_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ const int fcr31_pos = NUM_FPU_REGS * sizeof(elf_fpreg_t);
+ u32 fcr31;
+ int err;
+
+ BUG_ON(count % sizeof(elf_fpreg_t));
+
+ if (pos + count > sizeof(elf_fpregset_t))
+ return -EIO;
+
+ init_fp_ctx(target);
+
+ if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t))
+ err = fpr_set_fpa(target, &pos, &count, &kbuf, &ubuf);
+ else
+ err = fpr_set_msa(target, &pos, &count, &kbuf, &ubuf);
+ if (err)
+ return err;
+
+ if (count > 0) {
+ err = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ &fcr31,
+ fcr31_pos, fcr31_pos + sizeof(u32));
+ if (err)
+ return err;
+
+ ptrace_setfcr31(target, fcr31);
+ }
+
+ return err;
+}
+
enum mips_regset {
REGSET_GPR,
REGSET_FPR,
diff --git a/arch/parisc/include/asm/ldcw.h b/arch/parisc/include/asm/ldcw.h
index 8be707e..82dea14 100644
--- a/arch/parisc/include/asm/ldcw.h
+++ b/arch/parisc/include/asm/ldcw.h
@@ -11,6 +11,7 @@
for the semaphore. */
#define __PA_LDCW_ALIGNMENT 16
+#define __PA_LDCW_ALIGN_ORDER 4
#define __ldcw_align(a) ({ \
unsigned long __ret = (unsigned long) &(a)->lock[0]; \
__ret = (__ret + __PA_LDCW_ALIGNMENT - 1) \
@@ -28,6 +29,7 @@
ldcd). */
#define __PA_LDCW_ALIGNMENT 4
+#define __PA_LDCW_ALIGN_ORDER 2
#define __ldcw_align(a) (&(a)->slock)
#define __LDCW "ldcw,co"
diff --git a/arch/parisc/kernel/entry.S b/arch/parisc/kernel/entry.S
index 4fcff2d..e3d3e8e 100644
--- a/arch/parisc/kernel/entry.S
+++ b/arch/parisc/kernel/entry.S
@@ -35,6 +35,7 @@
#include <asm/pgtable.h>
#include <asm/signal.h>
#include <asm/unistd.h>
+#include <asm/ldcw.h>
#include <asm/thread_info.h>
#include <linux/linkage.h>
@@ -46,6 +47,14 @@
#endif
.import pa_tlb_lock,data
+ .macro load_pa_tlb_lock reg
+#if __PA_LDCW_ALIGNMENT > 4
+ load32 PA(pa_tlb_lock) + __PA_LDCW_ALIGNMENT-1, \reg
+ depi 0,31,__PA_LDCW_ALIGN_ORDER, \reg
+#else
+ load32 PA(pa_tlb_lock), \reg
+#endif
+ .endm
/* space_to_prot macro creates a prot id from a space id */
@@ -457,7 +466,7 @@
.macro tlb_lock spc,ptp,pte,tmp,tmp1,fault
#ifdef CONFIG_SMP
cmpib,COND(=),n 0,\spc,2f
- load32 PA(pa_tlb_lock),\tmp
+ load_pa_tlb_lock \tmp
1: LDCW 0(\tmp),\tmp1
cmpib,COND(=) 0,\tmp1,1b
nop
@@ -480,7 +489,7 @@
/* Release pa_tlb_lock lock. */
.macro tlb_unlock1 spc,tmp
#ifdef CONFIG_SMP
- load32 PA(pa_tlb_lock),\tmp
+ load_pa_tlb_lock \tmp
tlb_unlock0 \spc,\tmp
#endif
.endm
diff --git a/arch/parisc/kernel/pacache.S b/arch/parisc/kernel/pacache.S
index adf7187..2d40c4f 100644
--- a/arch/parisc/kernel/pacache.S
+++ b/arch/parisc/kernel/pacache.S
@@ -36,6 +36,7 @@
#include <asm/assembly.h>
#include <asm/pgtable.h>
#include <asm/cache.h>
+#include <asm/ldcw.h>
#include <linux/linkage.h>
.text
@@ -333,8 +334,12 @@
.macro tlb_lock la,flags,tmp
#ifdef CONFIG_SMP
- ldil L%pa_tlb_lock,%r1
- ldo R%pa_tlb_lock(%r1),\la
+#if __PA_LDCW_ALIGNMENT > 4
+ load32 pa_tlb_lock + __PA_LDCW_ALIGNMENT-1, \la
+ depi 0,31,__PA_LDCW_ALIGN_ORDER, \la
+#else
+ load32 pa_tlb_lock, \la
+#endif
rsm PSW_SM_I,\flags
1: LDCW 0(\la),\tmp
cmpib,<>,n 0,\tmp,3f
diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c
index 7593787..c3a532a 100644
--- a/arch/parisc/kernel/process.c
+++ b/arch/parisc/kernel/process.c
@@ -39,6 +39,7 @@
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/fs.h>
+#include <linux/cpu.h>
#include <linux/module.h>
#include <linux/personality.h>
#include <linux/ptrace.h>
@@ -181,6 +182,44 @@
}
/*
+ * Idle thread support
+ *
+ * Detect when running on QEMU with SeaBIOS PDC Firmware and let
+ * QEMU idle the host too.
+ */
+
+int running_on_qemu __read_mostly;
+
+void __cpuidle arch_cpu_idle_dead(void)
+{
+ /* nop on real hardware, qemu will offline CPU. */
+ asm volatile("or %%r31,%%r31,%%r31\n":::);
+}
+
+void __cpuidle arch_cpu_idle(void)
+{
+ local_irq_enable();
+
+ /* nop on real hardware, qemu will idle sleep. */
+ asm volatile("or %%r10,%%r10,%%r10\n":::);
+}
+
+static int __init parisc_idle_init(void)
+{
+ const char *marker;
+
+ /* check QEMU/SeaBIOS marker in PAGE0 */
+ marker = (char *) &PAGE0->pad0;
+ running_on_qemu = (memcmp(marker, "SeaBIOS", 8) == 0);
+
+ if (!running_on_qemu)
+ cpu_idle_poll_ctrl(1);
+
+ return 0;
+}
+arch_initcall(parisc_idle_init);
+
+/*
* Copy architecture-specific thread state
*/
int
diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c
index 0f9cd90..f06a9a0 100644
--- a/arch/s390/kernel/compat_linux.c
+++ b/arch/s390/kernel/compat_linux.c
@@ -263,6 +263,7 @@
return retval;
}
+ groups_sort(group_info);
retval = set_current_groups(group_info);
put_group_info(group_info);
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 64e9609d..d2f5372 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -46,7 +46,7 @@
select ARCH_USE_CMPXCHG_LOCKREF if X86_64
select ARCH_USE_QUEUED_RWLOCKS
select ARCH_USE_QUEUED_SPINLOCKS
- select ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH if SMP
+ select ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH
select ARCH_WANTS_DYNAMIC_TASK_STRUCT
select ARCH_WANT_FRAME_POINTERS
select ARCH_WANT_IPC_PARSE_VERSION if X86_32
@@ -65,6 +65,7 @@
select GENERIC_CLOCKEVENTS_MIN_ADJUST
select GENERIC_CMOS_UPDATE
select GENERIC_CPU_AUTOPROBE
+ select GENERIC_CPU_VULNERABILITIES
select GENERIC_EARLY_IOREMAP
select GENERIC_FIND_FIRST_BIT
select GENERIC_IOMAP
@@ -408,6 +409,19 @@
def_bool y
depends on X86_GOLDFISH
+config RETPOLINE
+ bool "Avoid speculative indirect branches in kernel"
+ default y
+ ---help---
+ Compile kernel with the retpoline compiler options to guard against
+ kernel-to-user data leaks by avoiding speculative indirect
+ branches. Requires a compiler with -mindirect-branch=thunk-extern
+ support for full protection. The kernel may run slower.
+
+ Without compiler support, at least indirect branches in assembler
+ code are eliminated. Since this includes the syscall entry path,
+ it is not entirely pointless.
+
if X86_32
config X86_EXTENDED_PLATFORM
bool "Support for extended (non-PC) x86 platforms"
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index 75725dc..10a7d0f 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -184,6 +184,14 @@
KBUILD_CFLAGS += $(mflags-y)
KBUILD_AFLAGS += $(mflags-y)
+# Avoid indirect branches in kernel to deal with Spectre
+ifdef CONFIG_RETPOLINE
+ RETPOLINE_CFLAGS += $(call cc-option,-mindirect-branch=thunk-extern -mindirect-branch-register)
+ ifneq ($(RETPOLINE_CFLAGS),)
+ KBUILD_CFLAGS += $(RETPOLINE_CFLAGS) -DRETPOLINE
+ endif
+endif
+
archscripts: scripts_basic
$(Q)$(MAKE) $(build)=arch/x86/tools relocs
diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h
index 766a521..2728e1b 100644
--- a/arch/x86/boot/compressed/misc.h
+++ b/arch/x86/boot/compressed/misc.h
@@ -9,6 +9,7 @@
*/
#undef CONFIG_PARAVIRT
#undef CONFIG_PARAVIRT_SPINLOCKS
+#undef CONFIG_PAGE_TABLE_ISOLATION
#undef CONFIG_KASAN
#include <linux/linkage.h>
diff --git a/arch/x86/crypto/aesni-intel_asm.S b/arch/x86/crypto/aesni-intel_asm.S
index 383a6f8..fa8801b 100644
--- a/arch/x86/crypto/aesni-intel_asm.S
+++ b/arch/x86/crypto/aesni-intel_asm.S
@@ -32,6 +32,7 @@
#include <linux/linkage.h>
#include <asm/inst.h>
#include <asm/frame.h>
+#include <asm/nospec-branch.h>
/*
* The following macros are used to move an (un)aligned 16 byte value to/from
@@ -2734,7 +2735,7 @@
pxor INC, STATE4
movdqu IV, 0x30(OUTP)
- call *%r11
+ CALL_NOSPEC %r11
movdqu 0x00(OUTP), INC
pxor INC, STATE1
@@ -2779,7 +2780,7 @@
_aesni_gf128mul_x_ble()
movups IV, (IVP)
- call *%r11
+ CALL_NOSPEC %r11
movdqu 0x40(OUTP), INC
pxor INC, STATE1
diff --git a/arch/x86/crypto/camellia-aesni-avx-asm_64.S b/arch/x86/crypto/camellia-aesni-avx-asm_64.S
index aa9e8bd..77ff4de 100644
--- a/arch/x86/crypto/camellia-aesni-avx-asm_64.S
+++ b/arch/x86/crypto/camellia-aesni-avx-asm_64.S
@@ -17,6 +17,7 @@
#include <linux/linkage.h>
#include <asm/frame.h>
+#include <asm/nospec-branch.h>
#define CAMELLIA_TABLE_BYTE_LEN 272
@@ -1224,7 +1225,7 @@
vpxor 14 * 16(%rax), %xmm15, %xmm14;
vpxor 15 * 16(%rax), %xmm15, %xmm15;
- call *%r9;
+ CALL_NOSPEC %r9;
addq $(16 * 16), %rsp;
diff --git a/arch/x86/crypto/camellia-aesni-avx2-asm_64.S b/arch/x86/crypto/camellia-aesni-avx2-asm_64.S
index 16186c1..7384342 100644
--- a/arch/x86/crypto/camellia-aesni-avx2-asm_64.S
+++ b/arch/x86/crypto/camellia-aesni-avx2-asm_64.S
@@ -12,6 +12,7 @@
#include <linux/linkage.h>
#include <asm/frame.h>
+#include <asm/nospec-branch.h>
#define CAMELLIA_TABLE_BYTE_LEN 272
@@ -1337,7 +1338,7 @@
vpxor 14 * 32(%rax), %ymm15, %ymm14;
vpxor 15 * 32(%rax), %ymm15, %ymm15;
- call *%r9;
+ CALL_NOSPEC %r9;
addq $(16 * 32), %rsp;
diff --git a/arch/x86/crypto/crc32c-pcl-intel-asm_64.S b/arch/x86/crypto/crc32c-pcl-intel-asm_64.S
index dc05f01..174fd41 100644
--- a/arch/x86/crypto/crc32c-pcl-intel-asm_64.S
+++ b/arch/x86/crypto/crc32c-pcl-intel-asm_64.S
@@ -45,6 +45,7 @@
#include <asm/inst.h>
#include <linux/linkage.h>
+#include <asm/nospec-branch.h>
## ISCSI CRC 32 Implementation with crc32 and pclmulqdq Instruction
@@ -172,7 +173,7 @@
movzxw (bufp, %rax, 2), len
lea crc_array(%rip), bufp
lea (bufp, len, 1), bufp
- jmp *bufp
+ JMP_NOSPEC bufp
################################################################
## 2a) PROCESS FULL BLOCKS:
diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S
index edba860..bdc9aea 100644
--- a/arch/x86/entry/entry_32.S
+++ b/arch/x86/entry/entry_32.S
@@ -45,6 +45,7 @@
#include <asm/asm.h>
#include <asm/smap.h>
#include <asm/export.h>
+#include <asm/nospec-branch.h>
.section .entry.text, "ax"
@@ -260,7 +261,7 @@
/* kernel thread */
1: movl %edi, %eax
- call *%ebx
+ CALL_NOSPEC %ebx
/*
* A kernel thread is allowed to return here after successfully
* calling do_execve(). Exit to userspace to complete the execve()
@@ -984,7 +985,8 @@
movl 0x4(%ebp), %edx
subl $MCOUNT_INSN_SIZE, %eax
- call *ftrace_trace_function
+ movl ftrace_trace_function, %ecx
+ CALL_NOSPEC %ecx
popl %edx
popl %ecx
@@ -1020,7 +1022,7 @@
movl %eax, %ecx
popl %edx
popl %eax
- jmp *%ecx
+ JMP_NOSPEC %ecx
#endif
#ifdef CONFIG_TRACING
@@ -1062,7 +1064,7 @@
movl %ecx, %es
TRACE_IRQS_OFF
movl %esp, %eax # pt_regs pointer
- call *%edi
+ CALL_NOSPEC %edi
jmp ret_from_exception
END(page_fault)
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index e7b0e7f..b9c901ce 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -36,6 +36,8 @@
#include <asm/smap.h>
#include <asm/pgtable_types.h>
#include <asm/export.h>
+#include <asm/kaiser.h>
+#include <asm/nospec-branch.h>
#include <linux/err.h>
/* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this. */
@@ -146,6 +148,7 @@
* it is too small to ever cause noticeable irq latency.
*/
SWAPGS_UNSAFE_STACK
+ SWITCH_KERNEL_CR3_NO_STACK
/*
* A hypervisor implementation might want to use a label
* after the swapgs, so that it can do the swapgs
@@ -206,7 +209,12 @@
* It might end up jumping to the slow path. If it jumps, RAX
* and all argument registers are clobbered.
*/
+#ifdef CONFIG_RETPOLINE
+ movq sys_call_table(, %rax, 8), %rax
+ call __x86_indirect_thunk_rax
+#else
call *sys_call_table(, %rax, 8)
+#endif
.Lentry_SYSCALL_64_after_fastpath_call:
movq %rax, RAX(%rsp)
@@ -228,6 +236,14 @@
movq RIP(%rsp), %rcx
movq EFLAGS(%rsp), %r11
RESTORE_C_REGS_EXCEPT_RCX_R11
+ /*
+ * This opens a window where we have a user CR3, but are
+ * running in the kernel. This makes using the CS
+ * register useless for telling whether or not we need to
+ * switch CR3 in NMIs. Normal interrupts are OK because
+ * they are off here.
+ */
+ SWITCH_USER_CR3
movq RSP(%rsp), %rsp
USERGS_SYSRET64
@@ -323,10 +339,26 @@
syscall_return_via_sysret:
/* rcx and r11 are already restored (see code above) */
RESTORE_C_REGS_EXCEPT_RCX_R11
+ /*
+ * This opens a window where we have a user CR3, but are
+ * running in the kernel. This makes using the CS
+ * register useless for telling whether or not we need to
+ * switch CR3 in NMIs. Normal interrupts are OK because
+ * they are off here.
+ */
+ SWITCH_USER_CR3
movq RSP(%rsp), %rsp
USERGS_SYSRET64
opportunistic_sysret_failed:
+ /*
+ * This opens a window where we have a user CR3, but are
+ * running in the kernel. This makes using the CS
+ * register useless for telling whether or not we need to
+ * switch CR3 in NMIs. Normal interrupts are OK because
+ * they are off here.
+ */
+ SWITCH_USER_CR3
SWAPGS
jmp restore_c_regs_and_iret
END(entry_SYSCALL_64)
@@ -354,7 +386,7 @@
jmp entry_SYSCALL64_slow_path
1:
- jmp *%rax /* Called from C */
+ JMP_NOSPEC %rax /* Called from C */
END(stub_ptregs_64)
.macro ptregs_stub func
@@ -424,13 +456,14 @@
movq %rsp, %rdi
call syscall_return_slowpath /* returns with IRQs disabled */
TRACE_IRQS_ON /* user mode is traced as IRQS on */
+ SWITCH_USER_CR3
SWAPGS
jmp restore_regs_and_iret
1:
/* kernel thread */
movq %r12, %rdi
- call *%rbx
+ CALL_NOSPEC %rbx
/*
* A kernel thread is allowed to return here after successfully
* calling do_execve(). Exit to userspace to complete the execve()
@@ -478,6 +511,7 @@
* tracking that we're in kernel mode.
*/
SWAPGS
+ SWITCH_KERNEL_CR3
/*
* We need to tell lockdep that IRQs are off. We can't do this until
@@ -535,6 +569,7 @@
mov %rsp,%rdi
call prepare_exit_to_usermode
TRACE_IRQS_IRETQ
+ SWITCH_USER_CR3
SWAPGS
jmp restore_regs_and_iret
@@ -612,6 +647,7 @@
pushq %rdi /* Stash user RDI */
SWAPGS
+ SWITCH_KERNEL_CR3
movq PER_CPU_VAR(espfix_waddr), %rdi
movq %rax, (0*8)(%rdi) /* user RAX */
movq (1*8)(%rsp), %rax /* user RIP */
@@ -638,6 +674,7 @@
* still points to an RO alias of the ESPFIX stack.
*/
orq PER_CPU_VAR(espfix_stack), %rax
+ SWITCH_USER_CR3
SWAPGS
movq %rax, %rsp
@@ -1022,7 +1059,11 @@
/*
* Save all registers in pt_regs, and switch gs if needed.
* Use slow, but surefire "are we in kernel?" check.
- * Return: ebx=0: need swapgs on exit, ebx=1: otherwise
+ *
+ * Return: ebx=0: needs swapgs but not SWITCH_USER_CR3 in paranoid_exit
+ * ebx=1: needs neither swapgs nor SWITCH_USER_CR3 in paranoid_exit
+ * ebx=2: needs both swapgs and SWITCH_USER_CR3 in paranoid_exit
+ * ebx=3: needs SWITCH_USER_CR3 but not swapgs in paranoid_exit
*/
ENTRY(paranoid_entry)
cld
@@ -1035,7 +1076,26 @@
js 1f /* negative -> in kernel */
SWAPGS
xorl %ebx, %ebx
-1: ret
+1:
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ /*
+ * We might have come in between a swapgs and a SWITCH_KERNEL_CR3
+ * on entry, or between a SWITCH_USER_CR3 and a swapgs on exit.
+ * Do a conditional SWITCH_KERNEL_CR3: this could safely be done
+ * unconditionally, but we need to find out whether the reverse
+ * should be done on return (conveyed to paranoid_exit in %ebx).
+ */
+ ALTERNATIVE "jmp 2f", "movq %cr3, %rax", X86_FEATURE_KAISER
+ testl $KAISER_SHADOW_PGD_OFFSET, %eax
+ jz 2f
+ orl $2, %ebx
+ andq $(~(X86_CR3_PCID_ASID_MASK | KAISER_SHADOW_PGD_OFFSET)), %rax
+ /* If PCID enabled, set X86_CR3_PCID_NOFLUSH_BIT */
+ ALTERNATIVE "", "bts $63, %rax", X86_FEATURE_PCID
+ movq %rax, %cr3
+2:
+#endif
+ ret
END(paranoid_entry)
/*
@@ -1048,19 +1108,26 @@
* be complicated. Fortunately, we there's no good reason
* to try to handle preemption here.
*
- * On entry, ebx is "no swapgs" flag (1: don't need swapgs, 0: need it)
+ * On entry: ebx=0: needs swapgs but not SWITCH_USER_CR3
+ * ebx=1: needs neither swapgs nor SWITCH_USER_CR3
+ * ebx=2: needs both swapgs and SWITCH_USER_CR3
+ * ebx=3: needs SWITCH_USER_CR3 but not swapgs
*/
ENTRY(paranoid_exit)
DISABLE_INTERRUPTS(CLBR_NONE)
TRACE_IRQS_OFF_DEBUG
- testl %ebx, %ebx /* swapgs needed? */
- jnz paranoid_exit_no_swapgs
- TRACE_IRQS_IRETQ
- SWAPGS_UNSAFE_STACK
- jmp paranoid_exit_restore
-paranoid_exit_no_swapgs:
TRACE_IRQS_IRETQ_DEBUG
-paranoid_exit_restore:
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ /* No ALTERNATIVE for X86_FEATURE_KAISER: paranoid_entry sets %ebx */
+ testl $2, %ebx /* SWITCH_USER_CR3 needed? */
+ jz paranoid_exit_no_switch
+ SWITCH_USER_CR3
+paranoid_exit_no_switch:
+#endif
+ testl $1, %ebx /* swapgs needed? */
+ jnz paranoid_exit_no_swapgs
+ SWAPGS_UNSAFE_STACK
+paranoid_exit_no_swapgs:
RESTORE_EXTRA_REGS
RESTORE_C_REGS
REMOVE_PT_GPREGS_FROM_STACK 8
@@ -1075,6 +1142,13 @@
cld
SAVE_C_REGS 8
SAVE_EXTRA_REGS 8
+ /*
+ * error_entry() always returns with a kernel gsbase and
+ * CR3. We must also have a kernel CR3/gsbase before
+ * calling TRACE_IRQS_*. Just unconditionally switch to
+ * the kernel CR3 here.
+ */
+ SWITCH_KERNEL_CR3
xorl %ebx, %ebx
testb $3, CS+8(%rsp)
jz .Lerror_kernelspace
@@ -1235,6 +1309,10 @@
*/
SWAPGS_UNSAFE_STACK
+ /*
+ * percpu variables are mapped with user CR3, so no need
+ * to switch CR3 here.
+ */
cld
movq %rsp, %rdx
movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp
@@ -1268,12 +1346,34 @@
movq %rsp, %rdi
movq $-1, %rsi
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ /* Unconditionally use kernel CR3 for do_nmi() */
+ /* %rax is saved above, so OK to clobber here */
+ ALTERNATIVE "jmp 2f", "movq %cr3, %rax", X86_FEATURE_KAISER
+ /* If PCID enabled, NOFLUSH now and NOFLUSH on return */
+ ALTERNATIVE "", "bts $63, %rax", X86_FEATURE_PCID
+ pushq %rax
+ /* mask off "user" bit of pgd address and 12 PCID bits: */
+ andq $(~(X86_CR3_PCID_ASID_MASK | KAISER_SHADOW_PGD_OFFSET)), %rax
+ movq %rax, %cr3
+2:
+#endif
call do_nmi
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ /*
+ * Unconditionally restore CR3. I know we return to
+ * kernel code that needs user CR3, but do we ever return
+ * to "user mode" where we need the kernel CR3?
+ */
+ ALTERNATIVE "", "popq %rax; movq %rax, %cr3", X86_FEATURE_KAISER
+#endif
+
/*
* Return back to user mode. We must *not* do the normal exit
- * work, because we don't want to enable interrupts. Fortunately,
- * do_nmi doesn't modify pt_regs.
+ * work, because we don't want to enable interrupts. Do not
+ * switch to user CR3: we might be going back to kernel code
+ * that had a user CR3 set.
*/
SWAPGS
jmp restore_c_regs_and_iret
@@ -1470,22 +1570,55 @@
ALLOC_PT_GPREGS_ON_STACK
/*
- * Use paranoid_entry to handle SWAPGS, but no need to use paranoid_exit
- * as we should not be calling schedule in NMI context.
- * Even with normal interrupts enabled. An NMI should not be
- * setting NEED_RESCHED or anything that normal interrupts and
- * exceptions might do.
+ * Use the same approach as paranoid_entry to handle SWAPGS, but
+ * without CR3 handling since we do that differently in NMIs. No
+ * need to use paranoid_exit as we should not be calling schedule
+ * in NMI context. Even with normal interrupts enabled. An NMI
+ * should not be setting NEED_RESCHED or anything that normal
+ * interrupts and exceptions might do.
*/
- call paranoid_entry
-
- /* paranoidentry do_nmi, 0; without TRACE_IRQS_OFF */
+ cld
+ SAVE_C_REGS
+ SAVE_EXTRA_REGS
+ movl $1, %ebx
+ movl $MSR_GS_BASE, %ecx
+ rdmsr
+ testl %edx, %edx
+ js 1f /* negative -> in kernel */
+ SWAPGS
+ xorl %ebx, %ebx
+1:
movq %rsp, %rdi
movq $-1, %rsi
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ /* Unconditionally use kernel CR3 for do_nmi() */
+ /* %rax is saved above, so OK to clobber here */
+ ALTERNATIVE "jmp 2f", "movq %cr3, %rax", X86_FEATURE_KAISER
+ /* If PCID enabled, NOFLUSH now and NOFLUSH on return */
+ ALTERNATIVE "", "bts $63, %rax", X86_FEATURE_PCID
+ pushq %rax
+ /* mask off "user" bit of pgd address and 12 PCID bits: */
+ andq $(~(X86_CR3_PCID_ASID_MASK | KAISER_SHADOW_PGD_OFFSET)), %rax
+ movq %rax, %cr3
+2:
+#endif
+
+ /* paranoidentry do_nmi, 0; without TRACE_IRQS_OFF */
call do_nmi
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ /*
+ * Unconditionally restore CR3. We might be returning to
+ * kernel code that needs user CR3, like just just before
+ * a sysret.
+ */
+ ALTERNATIVE "", "popq %rax; movq %rax, %cr3", X86_FEATURE_KAISER
+#endif
+
testl %ebx, %ebx /* swapgs needed? */
jnz nmi_restore
nmi_swapgs:
+ /* We fixed up CR3 above, so no need to switch it here */
SWAPGS_UNSAFE_STACK
nmi_restore:
RESTORE_EXTRA_REGS
diff --git a/arch/x86/entry/entry_64_compat.S b/arch/x86/entry/entry_64_compat.S
index e1721da..d76a976 100644
--- a/arch/x86/entry/entry_64_compat.S
+++ b/arch/x86/entry/entry_64_compat.S
@@ -13,6 +13,8 @@
#include <asm/irqflags.h>
#include <asm/asm.h>
#include <asm/smap.h>
+#include <asm/pgtable_types.h>
+#include <asm/kaiser.h>
#include <linux/linkage.h>
#include <linux/err.h>
@@ -48,6 +50,7 @@
ENTRY(entry_SYSENTER_compat)
/* Interrupts are off on entry. */
SWAPGS_UNSAFE_STACK
+ SWITCH_KERNEL_CR3_NO_STACK
movq PER_CPU_VAR(cpu_current_top_of_stack), %rsp
/*
@@ -184,6 +187,7 @@
ENTRY(entry_SYSCALL_compat)
/* Interrupts are off on entry. */
SWAPGS_UNSAFE_STACK
+ SWITCH_KERNEL_CR3_NO_STACK
/* Stash user ESP and switch to the kernel stack. */
movl %esp, %r8d
@@ -259,6 +263,7 @@
xorq %r8, %r8
xorq %r9, %r9
xorq %r10, %r10
+ SWITCH_USER_CR3
movq RSP-ORIG_RAX(%rsp), %rsp
swapgs
sysretl
@@ -297,7 +302,7 @@
PARAVIRT_ADJUST_EXCEPTION_FRAME
ASM_CLAC /* Do this early to minimize exposure */
SWAPGS
-
+ SWITCH_KERNEL_CR3_NO_STACK
/*
* User tracing code (ptrace or signal handlers) might assume that
* the saved RAX contains a 32-bit number when we're invoking a 32-bit
@@ -338,6 +343,7 @@
/* Go back to user mode. */
TRACE_IRQS_ON
+ SWITCH_USER_CR3
SWAPGS
jmp restore_regs_and_iret
END(entry_INT80_compat)
diff --git a/arch/x86/entry/vsyscall/vsyscall_64.c b/arch/x86/entry/vsyscall/vsyscall_64.c
index 636c4b3..6bb7e92 100644
--- a/arch/x86/entry/vsyscall/vsyscall_64.c
+++ b/arch/x86/entry/vsyscall/vsyscall_64.c
@@ -66,6 +66,11 @@
}
early_param("vsyscall", vsyscall_setup);
+bool vsyscall_enabled(void)
+{
+ return vsyscall_mode != NONE;
+}
+
static void warn_bad_vsyscall(const char *level, struct pt_regs *regs,
const char *message)
{
diff --git a/arch/x86/events/intel/ds.c b/arch/x86/events/intel/ds.c
index 9dfeeec..8e7a3f1 100644
--- a/arch/x86/events/intel/ds.c
+++ b/arch/x86/events/intel/ds.c
@@ -2,11 +2,15 @@
#include <linux/types.h>
#include <linux/slab.h>
+#include <asm/kaiser.h>
#include <asm/perf_event.h>
#include <asm/insn.h>
#include "../perf_event.h"
+static
+DEFINE_PER_CPU_SHARED_ALIGNED_USER_MAPPED(struct debug_store, cpu_debug_store);
+
/* The size of a BTS record in bytes: */
#define BTS_RECORD_SIZE 24
@@ -268,6 +272,39 @@
static DEFINE_PER_CPU(void *, insn_buffer);
+static void *dsalloc(size_t size, gfp_t flags, int node)
+{
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ unsigned int order = get_order(size);
+ struct page *page;
+ unsigned long addr;
+
+ page = __alloc_pages_node(node, flags | __GFP_ZERO, order);
+ if (!page)
+ return NULL;
+ addr = (unsigned long)page_address(page);
+ if (kaiser_add_mapping(addr, size, __PAGE_KERNEL) < 0) {
+ __free_pages(page, order);
+ addr = 0;
+ }
+ return (void *)addr;
+#else
+ return kmalloc_node(size, flags | __GFP_ZERO, node);
+#endif
+}
+
+static void dsfree(const void *buffer, size_t size)
+{
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ if (!buffer)
+ return;
+ kaiser_remove_mapping((unsigned long)buffer, size);
+ free_pages((unsigned long)buffer, get_order(size));
+#else
+ kfree(buffer);
+#endif
+}
+
static int alloc_pebs_buffer(int cpu)
{
struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds;
@@ -278,7 +315,7 @@
if (!x86_pmu.pebs)
return 0;
- buffer = kzalloc_node(x86_pmu.pebs_buffer_size, GFP_KERNEL, node);
+ buffer = dsalloc(x86_pmu.pebs_buffer_size, GFP_KERNEL, node);
if (unlikely(!buffer))
return -ENOMEM;
@@ -289,7 +326,7 @@
if (x86_pmu.intel_cap.pebs_format < 2) {
ibuffer = kzalloc_node(PEBS_FIXUP_SIZE, GFP_KERNEL, node);
if (!ibuffer) {
- kfree(buffer);
+ dsfree(buffer, x86_pmu.pebs_buffer_size);
return -ENOMEM;
}
per_cpu(insn_buffer, cpu) = ibuffer;
@@ -315,7 +352,8 @@
kfree(per_cpu(insn_buffer, cpu));
per_cpu(insn_buffer, cpu) = NULL;
- kfree((void *)(unsigned long)ds->pebs_buffer_base);
+ dsfree((void *)(unsigned long)ds->pebs_buffer_base,
+ x86_pmu.pebs_buffer_size);
ds->pebs_buffer_base = 0;
}
@@ -329,7 +367,7 @@
if (!x86_pmu.bts)
return 0;
- buffer = kzalloc_node(BTS_BUFFER_SIZE, GFP_KERNEL | __GFP_NOWARN, node);
+ buffer = dsalloc(BTS_BUFFER_SIZE, GFP_KERNEL | __GFP_NOWARN, node);
if (unlikely(!buffer)) {
WARN_ONCE(1, "%s: BTS buffer allocation failure\n", __func__);
return -ENOMEM;
@@ -355,19 +393,15 @@
if (!ds || !x86_pmu.bts)
return;
- kfree((void *)(unsigned long)ds->bts_buffer_base);
+ dsfree((void *)(unsigned long)ds->bts_buffer_base, BTS_BUFFER_SIZE);
ds->bts_buffer_base = 0;
}
static int alloc_ds_buffer(int cpu)
{
- int node = cpu_to_node(cpu);
- struct debug_store *ds;
+ struct debug_store *ds = per_cpu_ptr(&cpu_debug_store, cpu);
- ds = kzalloc_node(sizeof(*ds), GFP_KERNEL, node);
- if (unlikely(!ds))
- return -ENOMEM;
-
+ memset(ds, 0, sizeof(*ds));
per_cpu(cpu_hw_events, cpu).ds = ds;
return 0;
@@ -381,7 +415,6 @@
return;
per_cpu(cpu_hw_events, cpu).ds = NULL;
- kfree(ds);
}
void release_ds_buffers(void)
diff --git a/arch/x86/include/asm/alternative.h b/arch/x86/include/asm/alternative.h
index d4aea31..deca9b9 100644
--- a/arch/x86/include/asm/alternative.h
+++ b/arch/x86/include/asm/alternative.h
@@ -139,7 +139,7 @@
".popsection\n" \
".pushsection .altinstr_replacement, \"ax\"\n" \
ALTINSTR_REPLACEMENT(newinstr, feature, 1) \
- ".popsection"
+ ".popsection\n"
#define ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2)\
OLDINSTR_2(oldinstr, 1, 2) \
@@ -150,7 +150,7 @@
".pushsection .altinstr_replacement, \"ax\"\n" \
ALTINSTR_REPLACEMENT(newinstr1, feature1, 1) \
ALTINSTR_REPLACEMENT(newinstr2, feature2, 2) \
- ".popsection"
+ ".popsection\n"
/*
* Alternative instructions for different CPU types or capabilities.
diff --git a/arch/x86/include/asm/asm-prototypes.h b/arch/x86/include/asm/asm-prototypes.h
index 44b8762..b15aa40 100644
--- a/arch/x86/include/asm/asm-prototypes.h
+++ b/arch/x86/include/asm/asm-prototypes.h
@@ -10,7 +10,32 @@
#include <asm/pgtable.h>
#include <asm/special_insns.h>
#include <asm/preempt.h>
+#include <asm/asm.h>
#ifndef CONFIG_X86_CMPXCHG64
extern void cmpxchg8b_emu(void);
#endif
+
+#ifdef CONFIG_RETPOLINE
+#ifdef CONFIG_X86_32
+#define INDIRECT_THUNK(reg) extern asmlinkage void __x86_indirect_thunk_e ## reg(void);
+#else
+#define INDIRECT_THUNK(reg) extern asmlinkage void __x86_indirect_thunk_r ## reg(void);
+INDIRECT_THUNK(8)
+INDIRECT_THUNK(9)
+INDIRECT_THUNK(10)
+INDIRECT_THUNK(11)
+INDIRECT_THUNK(12)
+INDIRECT_THUNK(13)
+INDIRECT_THUNK(14)
+INDIRECT_THUNK(15)
+#endif
+INDIRECT_THUNK(ax)
+INDIRECT_THUNK(bx)
+INDIRECT_THUNK(cx)
+INDIRECT_THUNK(dx)
+INDIRECT_THUNK(si)
+INDIRECT_THUNK(di)
+INDIRECT_THUNK(bp)
+INDIRECT_THUNK(sp)
+#endif /* CONFIG_RETPOLINE */
diff --git a/arch/x86/include/asm/asm.h b/arch/x86/include/asm/asm.h
index 7acb51c..0052352 100644
--- a/arch/x86/include/asm/asm.h
+++ b/arch/x86/include/asm/asm.h
@@ -125,4 +125,15 @@
/* For C file, we already have NOKPROBE_SYMBOL macro */
#endif
+#ifndef __ASSEMBLY__
+/*
+ * This output constraint should be used for any inline asm which has a "call"
+ * instruction. Otherwise the asm may be inserted before the frame pointer
+ * gets set up by the containing function. If you forget to do this, objtool
+ * may print a "call without frame pointer save/setup" warning.
+ */
+register unsigned long current_stack_pointer asm(_ASM_SP);
+#define ASM_CALL_CONSTRAINT "+r" (current_stack_pointer)
+#endif
+
#endif /* _ASM_X86_ASM_H */
diff --git a/arch/x86/include/asm/cmdline.h b/arch/x86/include/asm/cmdline.h
index e01f7f7..84ae170 100644
--- a/arch/x86/include/asm/cmdline.h
+++ b/arch/x86/include/asm/cmdline.h
@@ -2,5 +2,7 @@
#define _ASM_X86_CMDLINE_H
int cmdline_find_option_bool(const char *cmdline_ptr, const char *option);
+int cmdline_find_option(const char *cmdline_ptr, const char *option,
+ char *buffer, int bufsize);
#endif /* _ASM_X86_CMDLINE_H */
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h
index 1d2b69f..9ea67a0 100644
--- a/arch/x86/include/asm/cpufeature.h
+++ b/arch/x86/include/asm/cpufeature.h
@@ -135,6 +135,8 @@
set_bit(bit, (unsigned long *)cpu_caps_set); \
} while (0)
+#define setup_force_cpu_bug(bit) setup_force_cpu_cap(bit)
+
#if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_X86_FAST_FEATURE_TESTS)
/*
* Static testing of CPU features. Used the same as boot_cpu_has().
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index ed10b5b..4467568 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -189,14 +189,21 @@
#define X86_FEATURE_CPB ( 7*32+ 2) /* AMD Core Performance Boost */
#define X86_FEATURE_EPB ( 7*32+ 3) /* IA32_ENERGY_PERF_BIAS support */
+#define X86_FEATURE_INVPCID_SINGLE ( 7*32+ 4) /* Effectively INVPCID && CR4.PCIDE=1 */
#define X86_FEATURE_HW_PSTATE ( 7*32+ 8) /* AMD HW-PState */
#define X86_FEATURE_PROC_FEEDBACK ( 7*32+ 9) /* AMD ProcFeedbackInterface */
+#define X86_FEATURE_RETPOLINE ( 7*32+12) /* Generic Retpoline mitigation for Spectre variant 2 */
+#define X86_FEATURE_RETPOLINE_AMD ( 7*32+13) /* AMD Retpoline mitigation for Spectre variant 2 */
+
#define X86_FEATURE_INTEL_PT ( 7*32+15) /* Intel Processor Trace */
#define X86_FEATURE_AVX512_4VNNIW (7*32+16) /* AVX-512 Neural Network Instructions */
#define X86_FEATURE_AVX512_4FMAPS (7*32+17) /* AVX-512 Multiply Accumulation Single precision */
+/* Because the ALTERNATIVE scheme is for members of the X86_FEATURE club... */
+#define X86_FEATURE_KAISER ( 7*32+31) /* CONFIG_PAGE_TABLE_ISOLATION w/o nokaiser */
+
/* Virtualization flags: Linux defined, word 8 */
#define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */
#define X86_FEATURE_VNMI ( 8*32+ 1) /* Intel Virtual NMI */
@@ -312,5 +319,8 @@
#define X86_BUG_SWAPGS_FENCE X86_BUG(11) /* SWAPGS without input dep on GS */
#define X86_BUG_MONITOR X86_BUG(12) /* IPI required to wake up remote CPU */
#define X86_BUG_AMD_E400 X86_BUG(13) /* CPU is among the affected by Erratum 400 */
+#define X86_BUG_CPU_MELTDOWN X86_BUG(14) /* CPU is affected by meltdown attack and needs kernel page table isolation */
+#define X86_BUG_SPECTRE_V1 X86_BUG(15) /* CPU is affected by Spectre variant 1 attack with conditional branches */
+#define X86_BUG_SPECTRE_V2 X86_BUG(16) /* CPU is affected by Spectre variant 2 attack with indirect branches */
#endif /* _ASM_X86_CPUFEATURES_H */
diff --git a/arch/x86/include/asm/desc.h b/arch/x86/include/asm/desc.h
index 12080d8..2ed5a2b 100644
--- a/arch/x86/include/asm/desc.h
+++ b/arch/x86/include/asm/desc.h
@@ -43,7 +43,7 @@
struct desc_struct gdt[GDT_ENTRIES];
} __attribute__((aligned(PAGE_SIZE)));
-DECLARE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page);
+DECLARE_PER_CPU_PAGE_ALIGNED_USER_MAPPED(struct gdt_page, gdt_page);
static inline struct desc_struct *get_cpu_gdt_table(unsigned int cpu)
{
diff --git a/arch/x86/include/asm/disabled-features.h b/arch/x86/include/asm/disabled-features.h
index 85599ad..21c5ac1 100644
--- a/arch/x86/include/asm/disabled-features.h
+++ b/arch/x86/include/asm/disabled-features.h
@@ -21,11 +21,13 @@
# define DISABLE_K6_MTRR (1<<(X86_FEATURE_K6_MTRR & 31))
# define DISABLE_CYRIX_ARR (1<<(X86_FEATURE_CYRIX_ARR & 31))
# define DISABLE_CENTAUR_MCR (1<<(X86_FEATURE_CENTAUR_MCR & 31))
+# define DISABLE_PCID 0
#else
# define DISABLE_VME 0
# define DISABLE_K6_MTRR 0
# define DISABLE_CYRIX_ARR 0
# define DISABLE_CENTAUR_MCR 0
+# define DISABLE_PCID (1<<(X86_FEATURE_PCID & 31))
#endif /* CONFIG_X86_64 */
#ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS
@@ -43,7 +45,7 @@
#define DISABLED_MASK1 0
#define DISABLED_MASK2 0
#define DISABLED_MASK3 (DISABLE_CYRIX_ARR|DISABLE_CENTAUR_MCR|DISABLE_K6_MTRR)
-#define DISABLED_MASK4 0
+#define DISABLED_MASK4 (DISABLE_PCID)
#define DISABLED_MASK5 0
#define DISABLED_MASK6 0
#define DISABLED_MASK7 0
diff --git a/arch/x86/include/asm/hardirq.h b/arch/x86/include/asm/hardirq.h
index 59405a2..9b76cd3 100644
--- a/arch/x86/include/asm/hardirq.h
+++ b/arch/x86/include/asm/hardirq.h
@@ -22,8 +22,8 @@
#ifdef CONFIG_SMP
unsigned int irq_resched_count;
unsigned int irq_call_count;
- unsigned int irq_tlb_count;
#endif
+ unsigned int irq_tlb_count;
#ifdef CONFIG_X86_THERMAL_VECTOR
unsigned int irq_thermal_count;
#endif
diff --git a/arch/x86/include/asm/hw_irq.h b/arch/x86/include/asm/hw_irq.h
index b90e105..0817d63 100644
--- a/arch/x86/include/asm/hw_irq.h
+++ b/arch/x86/include/asm/hw_irq.h
@@ -178,7 +178,7 @@
#define VECTOR_RETRIGGERED ((void *)~0UL)
typedef struct irq_desc* vector_irq_t[NR_VECTORS];
-DECLARE_PER_CPU(vector_irq_t, vector_irq);
+DECLARE_PER_CPU_USER_MAPPED(vector_irq_t, vector_irq);
#endif /* !ASSEMBLY_ */
diff --git a/arch/x86/include/asm/kaiser.h b/arch/x86/include/asm/kaiser.h
new file mode 100644
index 0000000..802bbbd
--- /dev/null
+++ b/arch/x86/include/asm/kaiser.h
@@ -0,0 +1,141 @@
+#ifndef _ASM_X86_KAISER_H
+#define _ASM_X86_KAISER_H
+
+#include <uapi/asm/processor-flags.h> /* For PCID constants */
+
+/*
+ * This file includes the definitions for the KAISER feature.
+ * KAISER is a counter measure against x86_64 side channel attacks on
+ * the kernel virtual memory. It has a shadow pgd for every process: the
+ * shadow pgd has a minimalistic kernel-set mapped, but includes the whole
+ * user memory. Within a kernel context switch, or when an interrupt is handled,
+ * the pgd is switched to the normal one. When the system switches to user mode,
+ * the shadow pgd is enabled. By this, the virtual memory caches are freed,
+ * and the user may not attack the whole kernel memory.
+ *
+ * A minimalistic kernel mapping holds the parts needed to be mapped in user
+ * mode, such as the entry/exit functions of the user space, or the stacks.
+ */
+
+#define KAISER_SHADOW_PGD_OFFSET 0x1000
+
+#ifdef __ASSEMBLY__
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+
+.macro _SWITCH_TO_KERNEL_CR3 reg
+movq %cr3, \reg
+andq $(~(X86_CR3_PCID_ASID_MASK | KAISER_SHADOW_PGD_OFFSET)), \reg
+/* If PCID enabled, set X86_CR3_PCID_NOFLUSH_BIT */
+ALTERNATIVE "", "bts $63, \reg", X86_FEATURE_PCID
+movq \reg, %cr3
+.endm
+
+.macro _SWITCH_TO_USER_CR3 reg regb
+/*
+ * regb must be the low byte portion of reg: because we have arranged
+ * for the low byte of the user PCID to serve as the high byte of NOFLUSH
+ * (0x80 for each when PCID is enabled, or 0x00 when PCID and NOFLUSH are
+ * not enabled): so that the one register can update both memory and cr3.
+ */
+movq %cr3, \reg
+orq PER_CPU_VAR(x86_cr3_pcid_user), \reg
+js 9f
+/* If PCID enabled, FLUSH this time, reset to NOFLUSH for next time */
+movb \regb, PER_CPU_VAR(x86_cr3_pcid_user+7)
+9:
+movq \reg, %cr3
+.endm
+
+.macro SWITCH_KERNEL_CR3
+ALTERNATIVE "jmp 8f", "pushq %rax", X86_FEATURE_KAISER
+_SWITCH_TO_KERNEL_CR3 %rax
+popq %rax
+8:
+.endm
+
+.macro SWITCH_USER_CR3
+ALTERNATIVE "jmp 8f", "pushq %rax", X86_FEATURE_KAISER
+_SWITCH_TO_USER_CR3 %rax %al
+popq %rax
+8:
+.endm
+
+.macro SWITCH_KERNEL_CR3_NO_STACK
+ALTERNATIVE "jmp 8f", \
+ __stringify(movq %rax, PER_CPU_VAR(unsafe_stack_register_backup)), \
+ X86_FEATURE_KAISER
+_SWITCH_TO_KERNEL_CR3 %rax
+movq PER_CPU_VAR(unsafe_stack_register_backup), %rax
+8:
+.endm
+
+#else /* CONFIG_PAGE_TABLE_ISOLATION */
+
+.macro SWITCH_KERNEL_CR3
+.endm
+.macro SWITCH_USER_CR3
+.endm
+.macro SWITCH_KERNEL_CR3_NO_STACK
+.endm
+
+#endif /* CONFIG_PAGE_TABLE_ISOLATION */
+
+#else /* __ASSEMBLY__ */
+
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+/*
+ * Upon kernel/user mode switch, it may happen that the address
+ * space has to be switched before the registers have been
+ * stored. To change the address space, another register is
+ * needed. A register therefore has to be stored/restored.
+*/
+DECLARE_PER_CPU_USER_MAPPED(unsigned long, unsafe_stack_register_backup);
+
+DECLARE_PER_CPU(unsigned long, x86_cr3_pcid_user);
+
+extern char __per_cpu_user_mapped_start[], __per_cpu_user_mapped_end[];
+
+extern int kaiser_enabled;
+extern void __init kaiser_check_boottime_disable(void);
+#else
+#define kaiser_enabled 0
+static inline void __init kaiser_check_boottime_disable(void) {}
+#endif /* CONFIG_PAGE_TABLE_ISOLATION */
+
+/*
+ * Kaiser function prototypes are needed even when CONFIG_PAGE_TABLE_ISOLATION is not set,
+ * so as to build with tests on kaiser_enabled instead of #ifdefs.
+ */
+
+/**
+ * kaiser_add_mapping - map a virtual memory part to the shadow (user) mapping
+ * @addr: the start address of the range
+ * @size: the size of the range
+ * @flags: The mapping flags of the pages
+ *
+ * The mapping is done on a global scope, so no bigger
+ * synchronization has to be done. the pages have to be
+ * manually unmapped again when they are not needed any longer.
+ */
+extern int kaiser_add_mapping(unsigned long addr, unsigned long size, unsigned long flags);
+
+/**
+ * kaiser_remove_mapping - unmap a virtual memory part of the shadow mapping
+ * @addr: the start address of the range
+ * @size: the size of the range
+ */
+extern void kaiser_remove_mapping(unsigned long start, unsigned long size);
+
+/**
+ * kaiser_init - Initialize the shadow mapping
+ *
+ * Most parts of the shadow mapping can be mapped upon boot
+ * time. Only per-process things like the thread stacks
+ * or a new LDT have to be mapped at runtime. These boot-
+ * time mappings are permanent and never unmapped.
+ */
+extern void kaiser_init(void);
+
+#endif /* __ASSEMBLY */
+
+#endif /* _ASM_X86_KAISER_H */
diff --git a/arch/x86/include/asm/mmu.h b/arch/x86/include/asm/mmu.h
index 72198c6..8b272a0 100644
--- a/arch/x86/include/asm/mmu.h
+++ b/arch/x86/include/asm/mmu.h
@@ -33,12 +33,6 @@
#endif
} mm_context_t;
-#ifdef CONFIG_SMP
void leave_mm(int cpu);
-#else
-static inline void leave_mm(int cpu)
-{
-}
-#endif
#endif /* _ASM_X86_MMU_H */
diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h
index f9dd224..d23e355 100644
--- a/arch/x86/include/asm/mmu_context.h
+++ b/arch/x86/include/asm/mmu_context.h
@@ -99,10 +99,8 @@
static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
{
-#ifdef CONFIG_SMP
if (this_cpu_read(cpu_tlbstate.state) == TLBSTATE_OK)
this_cpu_write(cpu_tlbstate.state, TLBSTATE_LAZY);
-#endif
}
static inline int init_new_context(struct task_struct *tsk,
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index b601dda..b11c4c0 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -330,6 +330,9 @@
#define FAM10H_MMIO_CONF_BASE_MASK 0xfffffffULL
#define FAM10H_MMIO_CONF_BASE_SHIFT 20
#define MSR_FAM10H_NODE_ID 0xc001100c
+#define MSR_F10H_DECFG 0xc0011029
+#define MSR_F10H_DECFG_LFENCE_SERIALIZE_BIT 1
+#define MSR_F10H_DECFG_LFENCE_SERIALIZE BIT_ULL(MSR_F10H_DECFG_LFENCE_SERIALIZE_BIT)
/* K8 MSRs */
#define MSR_K8_TOP_MEM1 0xc001001a
diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h
new file mode 100644
index 0000000..402a11c
--- /dev/null
+++ b/arch/x86/include/asm/nospec-branch.h
@@ -0,0 +1,214 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __NOSPEC_BRANCH_H__
+#define __NOSPEC_BRANCH_H__
+
+#include <asm/alternative.h>
+#include <asm/alternative-asm.h>
+#include <asm/cpufeatures.h>
+
+/*
+ * Fill the CPU return stack buffer.
+ *
+ * Each entry in the RSB, if used for a speculative 'ret', contains an
+ * infinite 'pause; jmp' loop to capture speculative execution.
+ *
+ * This is required in various cases for retpoline and IBRS-based
+ * mitigations for the Spectre variant 2 vulnerability. Sometimes to
+ * eliminate potentially bogus entries from the RSB, and sometimes
+ * purely to ensure that it doesn't get empty, which on some CPUs would
+ * allow predictions from other (unwanted!) sources to be used.
+ *
+ * We define a CPP macro such that it can be used from both .S files and
+ * inline assembly. It's possible to do a .macro and then include that
+ * from C via asm(".include <asm/nospec-branch.h>") but let's not go there.
+ */
+
+#define RSB_CLEAR_LOOPS 32 /* To forcibly overwrite all entries */
+#define RSB_FILL_LOOPS 16 /* To avoid underflow */
+
+/*
+ * Google experimented with loop-unrolling and this turned out to be
+ * the optimal version — two calls, each with their own speculation
+ * trap should their return address end up getting used, in a loop.
+ */
+#define __FILL_RETURN_BUFFER(reg, nr, sp) \
+ mov $(nr/2), reg; \
+771: \
+ call 772f; \
+773: /* speculation trap */ \
+ pause; \
+ jmp 773b; \
+772: \
+ call 774f; \
+775: /* speculation trap */ \
+ pause; \
+ jmp 775b; \
+774: \
+ dec reg; \
+ jnz 771b; \
+ add $(BITS_PER_LONG/8) * nr, sp;
+
+#ifdef __ASSEMBLY__
+
+/*
+ * This should be used immediately before a retpoline alternative. It tells
+ * objtool where the retpolines are so that it can make sense of the control
+ * flow by just reading the original instruction(s) and ignoring the
+ * alternatives.
+ */
+.macro ANNOTATE_NOSPEC_ALTERNATIVE
+ .Lannotate_\@:
+ .pushsection .discard.nospec
+ .long .Lannotate_\@ - .
+ .popsection
+.endm
+
+/*
+ * These are the bare retpoline primitives for indirect jmp and call.
+ * Do not use these directly; they only exist to make the ALTERNATIVE
+ * invocation below less ugly.
+ */
+.macro RETPOLINE_JMP reg:req
+ call .Ldo_rop_\@
+.Lspec_trap_\@:
+ pause
+ jmp .Lspec_trap_\@
+.Ldo_rop_\@:
+ mov \reg, (%_ASM_SP)
+ ret
+.endm
+
+/*
+ * This is a wrapper around RETPOLINE_JMP so the called function in reg
+ * returns to the instruction after the macro.
+ */
+.macro RETPOLINE_CALL reg:req
+ jmp .Ldo_call_\@
+.Ldo_retpoline_jmp_\@:
+ RETPOLINE_JMP \reg
+.Ldo_call_\@:
+ call .Ldo_retpoline_jmp_\@
+.endm
+
+/*
+ * JMP_NOSPEC and CALL_NOSPEC macros can be used instead of a simple
+ * indirect jmp/call which may be susceptible to the Spectre variant 2
+ * attack.
+ */
+.macro JMP_NOSPEC reg:req
+#ifdef CONFIG_RETPOLINE
+ ANNOTATE_NOSPEC_ALTERNATIVE
+ ALTERNATIVE_2 __stringify(jmp *\reg), \
+ __stringify(RETPOLINE_JMP \reg), X86_FEATURE_RETPOLINE, \
+ __stringify(lfence; jmp *\reg), X86_FEATURE_RETPOLINE_AMD
+#else
+ jmp *\reg
+#endif
+.endm
+
+.macro CALL_NOSPEC reg:req
+#ifdef CONFIG_RETPOLINE
+ ANNOTATE_NOSPEC_ALTERNATIVE
+ ALTERNATIVE_2 __stringify(call *\reg), \
+ __stringify(RETPOLINE_CALL \reg), X86_FEATURE_RETPOLINE,\
+ __stringify(lfence; call *\reg), X86_FEATURE_RETPOLINE_AMD
+#else
+ call *\reg
+#endif
+.endm
+
+ /*
+ * A simpler FILL_RETURN_BUFFER macro. Don't make people use the CPP
+ * monstrosity above, manually.
+ */
+.macro FILL_RETURN_BUFFER reg:req nr:req ftr:req
+#ifdef CONFIG_RETPOLINE
+ ANNOTATE_NOSPEC_ALTERNATIVE
+ ALTERNATIVE "jmp .Lskip_rsb_\@", \
+ __stringify(__FILL_RETURN_BUFFER(\reg,\nr,%_ASM_SP)) \
+ \ftr
+.Lskip_rsb_\@:
+#endif
+.endm
+
+#else /* __ASSEMBLY__ */
+
+#define ANNOTATE_NOSPEC_ALTERNATIVE \
+ "999:\n\t" \
+ ".pushsection .discard.nospec\n\t" \
+ ".long 999b - .\n\t" \
+ ".popsection\n\t"
+
+#if defined(CONFIG_X86_64) && defined(RETPOLINE)
+
+/*
+ * Since the inline asm uses the %V modifier which is only in newer GCC,
+ * the 64-bit one is dependent on RETPOLINE not CONFIG_RETPOLINE.
+ */
+# define CALL_NOSPEC \
+ ANNOTATE_NOSPEC_ALTERNATIVE \
+ ALTERNATIVE( \
+ "call *%[thunk_target]\n", \
+ "call __x86_indirect_thunk_%V[thunk_target]\n", \
+ X86_FEATURE_RETPOLINE)
+# define THUNK_TARGET(addr) [thunk_target] "r" (addr)
+
+#elif defined(CONFIG_X86_32) && defined(CONFIG_RETPOLINE)
+/*
+ * For i386 we use the original ret-equivalent retpoline, because
+ * otherwise we'll run out of registers. We don't care about CET
+ * here, anyway.
+ */
+# define CALL_NOSPEC ALTERNATIVE("call *%[thunk_target]\n", \
+ " jmp 904f;\n" \
+ " .align 16\n" \
+ "901: call 903f;\n" \
+ "902: pause;\n" \
+ " jmp 902b;\n" \
+ " .align 16\n" \
+ "903: addl $4, %%esp;\n" \
+ " pushl %[thunk_target];\n" \
+ " ret;\n" \
+ " .align 16\n" \
+ "904: call 901b;\n", \
+ X86_FEATURE_RETPOLINE)
+
+# define THUNK_TARGET(addr) [thunk_target] "rm" (addr)
+#else /* No retpoline for C / inline asm */
+# define CALL_NOSPEC "call *%[thunk_target]\n"
+# define THUNK_TARGET(addr) [thunk_target] "rm" (addr)
+#endif
+
+/* The Spectre V2 mitigation variants */
+enum spectre_v2_mitigation {
+ SPECTRE_V2_NONE,
+ SPECTRE_V2_RETPOLINE_MINIMAL,
+ SPECTRE_V2_RETPOLINE_MINIMAL_AMD,
+ SPECTRE_V2_RETPOLINE_GENERIC,
+ SPECTRE_V2_RETPOLINE_AMD,
+ SPECTRE_V2_IBRS,
+};
+
+/*
+ * On VMEXIT we must ensure that no RSB predictions learned in the guest
+ * can be followed in the host, by overwriting the RSB completely. Both
+ * retpoline and IBRS mitigations for Spectre v2 need this; only on future
+ * CPUs with IBRS_ATT *might* it be avoided.
+ */
+static inline void vmexit_fill_RSB(void)
+{
+#ifdef CONFIG_RETPOLINE
+ unsigned long loops = RSB_CLEAR_LOOPS / 2;
+
+ asm volatile (ANNOTATE_NOSPEC_ALTERNATIVE
+ ALTERNATIVE("jmp 910f",
+ __stringify(__FILL_RETURN_BUFFER(%0, RSB_CLEAR_LOOPS, %1)),
+ X86_FEATURE_RETPOLINE)
+ "910:"
+ : "=&r" (loops), ASM_CALL_CONSTRAINT
+ : "r" (loops) : "memory" );
+#endif
+}
+#endif /* __ASSEMBLY__ */
+#endif /* __NOSPEC_BRANCH_H__ */
diff --git a/arch/x86/include/asm/pgalloc.h b/arch/x86/include/asm/pgalloc.h
index b6d4259..1178a51 100644
--- a/arch/x86/include/asm/pgalloc.h
+++ b/arch/x86/include/asm/pgalloc.h
@@ -27,6 +27,17 @@
*/
extern gfp_t __userpte_alloc_gfp;
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+/*
+ * Instead of one PGD, we acquire two PGDs. Being order-1, it is
+ * both 8k in size and 8k-aligned. That lets us just flip bit 12
+ * in a pointer to swap between the two 4k halves.
+ */
+#define PGD_ALLOCATION_ORDER 1
+#else
+#define PGD_ALLOCATION_ORDER 0
+#endif
+
/*
* Allocate and free page tables.
*/
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index 437feb4..2536f90 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -18,6 +18,12 @@
#ifndef __ASSEMBLY__
#include <asm/x86_init.h>
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+extern int kaiser_enabled;
+#else
+#define kaiser_enabled 0
+#endif
+
void ptdump_walk_pgd_level(struct seq_file *m, pgd_t *pgd);
void ptdump_walk_pgd_level_checkwx(void);
@@ -690,7 +696,17 @@
static inline int pgd_bad(pgd_t pgd)
{
- return (pgd_flags(pgd) & ~_PAGE_USER) != _KERNPG_TABLE;
+ pgdval_t ignore_flags = _PAGE_USER;
+ /*
+ * We set NX on KAISER pgds that map userspace memory so
+ * that userspace can not meaningfully use the kernel
+ * page table by accident; it will fault on the first
+ * instruction it tries to run. See native_set_pgd().
+ */
+ if (kaiser_enabled)
+ ignore_flags |= _PAGE_NX;
+
+ return (pgd_flags(pgd) & ~ignore_flags) != _KERNPG_TABLE;
}
static inline int pgd_none(pgd_t pgd)
@@ -903,7 +919,15 @@
*/
static inline void clone_pgd_range(pgd_t *dst, pgd_t *src, int count)
{
- memcpy(dst, src, count * sizeof(pgd_t));
+ memcpy(dst, src, count * sizeof(pgd_t));
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+ if (kaiser_enabled) {
+ /* Clone the shadow pgd part as well */
+ memcpy(native_get_shadow_pgd(dst),
+ native_get_shadow_pgd(src),
+ count * sizeof(pgd_t));
+ }
+#endif
}
#define PTE_SHIFT ilog2(PTRS_PER_PTE)
diff --git a/arch/x86/include/asm/pgtable_64.h b/arch/x86/include/asm/pgtable_64.h
index 1cc82ec..ce97c8c6 100644
--- a/arch/x86/include/asm/pgtable_64.h
+++ b/arch/x86/include/asm/pgtable_64.h
@@ -106,9 +106,32 @@
native_set_pud(pud, native_make_pud(0));
}
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+extern pgd_t kaiser_set_shadow_pgd(pgd_t *pgdp, pgd_t pgd);
+
+static inline pgd_t *native_get_shadow_pgd(pgd_t *pgdp)
+{
+#ifdef CONFIG_DEBUG_VM
+ /* linux/mmdebug.h may not have been included at this point */
+ BUG_ON(!kaiser_enabled);
+#endif
+ return (pgd_t *)((unsigned long)pgdp | (unsigned long)PAGE_SIZE);
+}
+#else
+static inline pgd_t kaiser_set_shadow_pgd(pgd_t *pgdp, pgd_t pgd)
+{
+ return pgd;
+}
+static inline pgd_t *native_get_shadow_pgd(pgd_t *pgdp)
+{
+ BUILD_BUG_ON(1);
+ return NULL;
+}
+#endif /* CONFIG_PAGE_TABLE_ISOLATION */
+
static inline void native_set_pgd(pgd_t *pgdp, pgd_t pgd)
{
- *pgdp = pgd;
+ *pgdp = kaiser_set_shadow_pgd(pgdp, pgd);
}
static inline void native_pgd_clear(pgd_t *pgd)
diff --git a/arch/x86/include/asm/pgtable_types.h b/arch/x86/include/asm/pgtable_types.h
index 8b4de22..f1c8ac4 100644
--- a/arch/x86/include/asm/pgtable_types.h
+++ b/arch/x86/include/asm/pgtable_types.h
@@ -119,7 +119,7 @@
#define _PAGE_DEVMAP (_AT(pteval_t, 0))
#endif
-#define _PAGE_PROTNONE (_AT(pteval_t, 1) << _PAGE_BIT_PROTNONE)
+#define _PAGE_PROTNONE (_AT(pteval_t, 1) << _PAGE_BIT_PROTNONE)
#define _PAGE_TABLE (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | \
_PAGE_ACCESSED | _PAGE_DIRTY)
@@ -137,6 +137,33 @@
_PAGE_SOFT_DIRTY)
#define _HPAGE_CHG_MASK (_PAGE_CHG_MASK | _PAGE_PSE)
+/* The ASID is the lower 12 bits of CR3 */
+#define X86_CR3_PCID_ASID_MASK (_AC((1<<12)-1,UL))
+
+/* Mask for all the PCID-related bits in CR3: */
+#define X86_CR3_PCID_MASK (X86_CR3_PCID_NOFLUSH | X86_CR3_PCID_ASID_MASK)
+#define X86_CR3_PCID_ASID_KERN (_AC(0x0,UL))
+
+#if defined(CONFIG_PAGE_TABLE_ISOLATION) && defined(CONFIG_X86_64)
+/* Let X86_CR3_PCID_ASID_USER be usable for the X86_CR3_PCID_NOFLUSH bit */
+#define X86_CR3_PCID_ASID_USER (_AC(0x80,UL))
+
+#define X86_CR3_PCID_KERN_FLUSH (X86_CR3_PCID_ASID_KERN)
+#define X86_CR3_PCID_USER_FLUSH (X86_CR3_PCID_ASID_USER)
+#define X86_CR3_PCID_KERN_NOFLUSH (X86_CR3_PCID_NOFLUSH | X86_CR3_PCID_ASID_KERN)
+#define X86_CR3_PCID_USER_NOFLUSH (X86_CR3_PCID_NOFLUSH | X86_CR3_PCID_ASID_USER)
+#else
+#define X86_CR3_PCID_ASID_USER (_AC(0x0,UL))
+/*
+ * PCIDs are unsupported on 32-bit and none of these bits can be
+ * set in CR3:
+ */
+#define X86_CR3_PCID_KERN_FLUSH (0)
+#define X86_CR3_PCID_USER_FLUSH (0)
+#define X86_CR3_PCID_KERN_NOFLUSH (0)
+#define X86_CR3_PCID_USER_NOFLUSH (0)
+#endif
+
/*
* The cache modes defined here are used to translate between pure SW usage
* and the HW defined cache mode bits and/or PAT entries.
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index 83db0ea..e40b19c 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -156,8 +156,8 @@
extern struct cpuinfo_x86 new_cpu_data;
extern struct tss_struct doublefault_tss;
-extern __u32 cpu_caps_cleared[NCAPINTS];
-extern __u32 cpu_caps_set[NCAPINTS];
+extern __u32 cpu_caps_cleared[NCAPINTS + NBUGINTS];
+extern __u32 cpu_caps_set[NCAPINTS + NBUGINTS];
#ifdef CONFIG_SMP
DECLARE_PER_CPU_READ_MOSTLY(struct cpuinfo_x86, cpu_info);
@@ -308,7 +308,7 @@
} ____cacheline_aligned;
-DECLARE_PER_CPU_SHARED_ALIGNED(struct tss_struct, cpu_tss);
+DECLARE_PER_CPU_SHARED_ALIGNED_USER_MAPPED(struct tss_struct, cpu_tss);
#ifdef CONFIG_X86_32
DECLARE_PER_CPU(unsigned long, cpu_current_top_of_stack);
diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h
index ad6f5eb0..bdf9c4c 100644
--- a/arch/x86/include/asm/thread_info.h
+++ b/arch/x86/include/asm/thread_info.h
@@ -152,17 +152,6 @@
*/
#ifndef __ASSEMBLY__
-static inline unsigned long current_stack_pointer(void)
-{
- unsigned long sp;
-#ifdef CONFIG_X86_64
- asm("mov %%rsp,%0" : "=g" (sp));
-#else
- asm("mov %%esp,%0" : "=g" (sp));
-#endif
- return sp;
-}
-
/*
* Walks up the stack frames to make sure that the specified object is
* entirely contained by a single stack frame.
diff --git a/arch/x86/include/asm/tlbflush.h b/arch/x86/include/asm/tlbflush.h
index fc5abff..94146f6 100644
--- a/arch/x86/include/asm/tlbflush.h
+++ b/arch/x86/include/asm/tlbflush.h
@@ -7,6 +7,7 @@
#include <asm/processor.h>
#include <asm/cpufeature.h>
#include <asm/special_insns.h>
+#include <asm/smp.h>
static inline void __invpcid(unsigned long pcid, unsigned long addr,
unsigned long type)
@@ -65,10 +66,8 @@
#endif
struct tlb_state {
-#ifdef CONFIG_SMP
struct mm_struct *active_mm;
int state;
-#endif
/*
* Access to this CR4 shadow and to H/W CR4 is protected by
@@ -133,6 +132,24 @@
cr4_set_bits(mask);
}
+/*
+ * Declare a couple of kaiser interfaces here for convenience,
+ * to avoid the need for asm/kaiser.h in unexpected places.
+ */
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+extern int kaiser_enabled;
+extern void kaiser_setup_pcid(void);
+extern void kaiser_flush_tlb_on_return_to_user(void);
+#else
+#define kaiser_enabled 0
+static inline void kaiser_setup_pcid(void)
+{
+}
+static inline void kaiser_flush_tlb_on_return_to_user(void)
+{
+}
+#endif
+
static inline void __native_flush_tlb(void)
{
/*
@@ -141,6 +158,8 @@
* back:
*/
preempt_disable();
+ if (kaiser_enabled)
+ kaiser_flush_tlb_on_return_to_user();
native_write_cr3(native_read_cr3());
preempt_enable();
}
@@ -150,20 +169,27 @@
unsigned long cr4;
cr4 = this_cpu_read(cpu_tlbstate.cr4);
- /* clear PGE */
- native_write_cr4(cr4 & ~X86_CR4_PGE);
- /* write old PGE again and flush TLBs */
- native_write_cr4(cr4);
+ if (cr4 & X86_CR4_PGE) {
+ /* clear PGE and flush TLB of all entries */
+ native_write_cr4(cr4 & ~X86_CR4_PGE);
+ /* restore PGE as it was before */
+ native_write_cr4(cr4);
+ } else {
+ /* do it with cr3, letting kaiser flush user PCID */
+ __native_flush_tlb();
+ }
}
static inline void __native_flush_tlb_global(void)
{
unsigned long flags;
- if (static_cpu_has(X86_FEATURE_INVPCID)) {
+ if (this_cpu_has(X86_FEATURE_INVPCID)) {
/*
* Using INVPCID is considerably faster than a pair of writes
* to CR4 sandwiched inside an IRQ flag save/restore.
+ *
+ * Note, this works with CR4.PCIDE=0 or 1.
*/
invpcid_flush_all();
return;
@@ -175,23 +201,52 @@
* be called from deep inside debugging code.)
*/
raw_local_irq_save(flags);
-
__native_flush_tlb_global_irq_disabled();
-
raw_local_irq_restore(flags);
}
static inline void __native_flush_tlb_single(unsigned long addr)
{
- asm volatile("invlpg (%0)" ::"r" (addr) : "memory");
+ /*
+ * SIMICS #GP's if you run INVPCID with type 2/3
+ * and X86_CR4_PCIDE clear. Shame!
+ *
+ * The ASIDs used below are hard-coded. But, we must not
+ * call invpcid(type=1/2) before CR4.PCIDE=1. Just call
+ * invlpg in the case we are called early.
+ */
+
+ if (!this_cpu_has(X86_FEATURE_INVPCID_SINGLE)) {
+ if (kaiser_enabled)
+ kaiser_flush_tlb_on_return_to_user();
+ asm volatile("invlpg (%0)" ::"r" (addr) : "memory");
+ return;
+ }
+ /* Flush the address out of both PCIDs. */
+ /*
+ * An optimization here might be to determine addresses
+ * that are only kernel-mapped and only flush the kernel
+ * ASID. But, userspace flushes are probably much more
+ * important performance-wise.
+ *
+ * Make sure to do only a single invpcid when KAISER is
+ * disabled and we have only a single ASID.
+ */
+ if (kaiser_enabled)
+ invpcid_flush_one(X86_CR3_PCID_ASID_USER, addr);
+ invpcid_flush_one(X86_CR3_PCID_ASID_KERN, addr);
}
static inline void __flush_tlb_all(void)
{
- if (boot_cpu_has(X86_FEATURE_PGE))
- __flush_tlb_global();
- else
- __flush_tlb();
+ __flush_tlb_global();
+ /*
+ * Note: if we somehow had PCID but not PGE, then this wouldn't work --
+ * we'd end up flushing kernel translations for the current ASID but
+ * we might fail to flush kernel translations for other cached ASIDs.
+ *
+ * To avoid this issue, we force PCID off if PGE is off.
+ */
}
static inline void __flush_tlb_one(unsigned long addr)
@@ -205,7 +260,6 @@
/*
* TLB flushing:
*
- * - flush_tlb() flushes the current mm struct TLBs
* - flush_tlb_all() flushes all processes TLBs
* - flush_tlb_mm(mm) flushes the specified mm context TLB's
* - flush_tlb_page(vma, vmaddr) flushes one page
@@ -217,84 +271,6 @@
* and page-granular flushes are available only on i486 and up.
*/
-#ifndef CONFIG_SMP
-
-/* "_up" is for UniProcessor.
- *
- * This is a helper for other header functions. *Not* intended to be called
- * directly. All global TLB flushes need to either call this, or to bump the
- * vm statistics themselves.
- */
-static inline void __flush_tlb_up(void)
-{
- count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ALL);
- __flush_tlb();
-}
-
-static inline void flush_tlb_all(void)
-{
- count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ALL);
- __flush_tlb_all();
-}
-
-static inline void flush_tlb(void)
-{
- __flush_tlb_up();
-}
-
-static inline void local_flush_tlb(void)
-{
- __flush_tlb_up();
-}
-
-static inline void flush_tlb_mm(struct mm_struct *mm)
-{
- if (mm == current->active_mm)
- __flush_tlb_up();
-}
-
-static inline void flush_tlb_page(struct vm_area_struct *vma,
- unsigned long addr)
-{
- if (vma->vm_mm == current->active_mm)
- __flush_tlb_one(addr);
-}
-
-static inline void flush_tlb_range(struct vm_area_struct *vma,
- unsigned long start, unsigned long end)
-{
- if (vma->vm_mm == current->active_mm)
- __flush_tlb_up();
-}
-
-static inline void flush_tlb_mm_range(struct mm_struct *mm,
- unsigned long start, unsigned long end, unsigned long vmflag)
-{
- if (mm == current->active_mm)
- __flush_tlb_up();
-}
-
-static inline void native_flush_tlb_others(const struct cpumask *cpumask,
- struct mm_struct *mm,
- unsigned long start,
- unsigned long end)
-{
-}
-
-static inline void reset_lazy_tlbstate(void)
-{
-}
-
-static inline void flush_tlb_kernel_range(unsigned long start,
- unsigned long end)
-{
- flush_tlb_all();
-}
-
-#else /* SMP */
-
-#include <asm/smp.h>
-
#define local_flush_tlb() __flush_tlb()
#define flush_tlb_mm(mm) flush_tlb_mm_range(mm, 0UL, TLB_FLUSH_ALL, 0UL)
@@ -303,13 +279,14 @@
flush_tlb_mm_range(vma->vm_mm, start, end, vma->vm_flags)
extern void flush_tlb_all(void);
-extern void flush_tlb_current_task(void);
-extern void flush_tlb_page(struct vm_area_struct *, unsigned long);
extern void flush_tlb_mm_range(struct mm_struct *mm, unsigned long start,
unsigned long end, unsigned long vmflag);
extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
-#define flush_tlb() flush_tlb_current_task()
+static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long a)
+{
+ flush_tlb_mm_range(vma->vm_mm, a, a + PAGE_SIZE, VM_NONE);
+}
void native_flush_tlb_others(const struct cpumask *cpumask,
struct mm_struct *mm,
@@ -324,8 +301,6 @@
this_cpu_write(cpu_tlbstate.active_mm, &init_mm);
}
-#endif /* SMP */
-
#ifndef CONFIG_PARAVIRT
#define flush_tlb_others(mask, mm, start, end) \
native_flush_tlb_others(mask, mm, start, end)
diff --git a/arch/x86/include/asm/vsyscall.h b/arch/x86/include/asm/vsyscall.h
index 6ba66ee..4865e10 100644
--- a/arch/x86/include/asm/vsyscall.h
+++ b/arch/x86/include/asm/vsyscall.h
@@ -12,12 +12,14 @@
* Returns true if handled.
*/
extern bool emulate_vsyscall(struct pt_regs *regs, unsigned long address);
+extern bool vsyscall_enabled(void);
#else
static inline void map_vsyscall(void) {}
static inline bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
{
return false;
}
+static inline bool vsyscall_enabled(void) { return false; }
#endif
#endif /* _ASM_X86_VSYSCALL_H */
diff --git a/arch/x86/include/asm/xen/hypercall.h b/arch/x86/include/asm/xen/hypercall.h
index 8b678af..ccdc23d 100644
--- a/arch/x86/include/asm/xen/hypercall.h
+++ b/arch/x86/include/asm/xen/hypercall.h
@@ -44,6 +44,7 @@
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/smap.h>
+#include <asm/nospec-branch.h>
#include <xen/interface/xen.h>
#include <xen/interface/sched.h>
@@ -216,9 +217,9 @@
__HYPERCALL_5ARG(a1, a2, a3, a4, a5);
stac();
- asm volatile("call *%[call]"
+ asm volatile(CALL_NOSPEC
: __HYPERCALL_5PARAM
- : [call] "a" (&hypercall_page[call])
+ : [thunk_target] "a" (&hypercall_page[call])
: __HYPERCALL_CLOBBER5);
clac();
diff --git a/arch/x86/include/uapi/asm/processor-flags.h b/arch/x86/include/uapi/asm/processor-flags.h
index 567de50..6768d13 100644
--- a/arch/x86/include/uapi/asm/processor-flags.h
+++ b/arch/x86/include/uapi/asm/processor-flags.h
@@ -77,7 +77,8 @@
#define X86_CR3_PWT _BITUL(X86_CR3_PWT_BIT)
#define X86_CR3_PCD_BIT 4 /* Page Cache Disable */
#define X86_CR3_PCD _BITUL(X86_CR3_PCD_BIT)
-#define X86_CR3_PCID_MASK _AC(0x00000fff,UL) /* PCID Mask */
+#define X86_CR3_PCID_NOFLUSH_BIT 63 /* Preserve old PCID */
+#define X86_CR3_PCID_NOFLUSH _BITULL(X86_CR3_PCID_NOFLUSH_BIT)
/*
* Intel CPU features in CR4
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index 11cc600..0a1e8a6 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -335,13 +335,12 @@
#ifdef CONFIG_X86_IO_APIC
#define MP_ISA_BUS 0
+static int __init mp_register_ioapic_irq(u8 bus_irq, u8 polarity,
+ u8 trigger, u32 gsi);
+
static void __init mp_override_legacy_irq(u8 bus_irq, u8 polarity, u8 trigger,
u32 gsi)
{
- int ioapic;
- int pin;
- struct mpc_intsrc mp_irq;
-
/*
* Check bus_irq boundary.
*/
@@ -351,14 +350,6 @@
}
/*
- * Convert 'gsi' to 'ioapic.pin'.
- */
- ioapic = mp_find_ioapic(gsi);
- if (ioapic < 0)
- return;
- pin = mp_find_ioapic_pin(ioapic, gsi);
-
- /*
* TBD: This check is for faulty timer entries, where the override
* erroneously sets the trigger to level, resulting in a HUGE
* increase of timer interrupts!
@@ -366,16 +357,8 @@
if ((bus_irq == 0) && (trigger == 3))
trigger = 1;
- mp_irq.type = MP_INTSRC;
- mp_irq.irqtype = mp_INT;
- mp_irq.irqflag = (trigger << 2) | polarity;
- mp_irq.srcbus = MP_ISA_BUS;
- mp_irq.srcbusirq = bus_irq; /* IRQ */
- mp_irq.dstapic = mpc_ioapic_id(ioapic); /* APIC ID */
- mp_irq.dstirq = pin; /* INTIN# */
-
- mp_save_irq(&mp_irq);
-
+ if (mp_register_ioapic_irq(bus_irq, polarity, trigger, gsi) < 0)
+ return;
/*
* Reset default identity mapping if gsi is also an legacy IRQ,
* otherwise there will be more than one entry with the same GSI
@@ -422,6 +405,34 @@
return 0;
}
+static int __init mp_register_ioapic_irq(u8 bus_irq, u8 polarity,
+ u8 trigger, u32 gsi)
+{
+ struct mpc_intsrc mp_irq;
+ int ioapic, pin;
+
+ /* Convert 'gsi' to 'ioapic.pin'(INTIN#) */
+ ioapic = mp_find_ioapic(gsi);
+ if (ioapic < 0) {
+ pr_warn("Failed to find ioapic for gsi : %u\n", gsi);
+ return ioapic;
+ }
+
+ pin = mp_find_ioapic_pin(ioapic, gsi);
+
+ mp_irq.type = MP_INTSRC;
+ mp_irq.irqtype = mp_INT;
+ mp_irq.irqflag = (trigger << 2) | polarity;
+ mp_irq.srcbus = MP_ISA_BUS;
+ mp_irq.srcbusirq = bus_irq;
+ mp_irq.dstapic = mpc_ioapic_id(ioapic);
+ mp_irq.dstirq = pin;
+
+ mp_save_irq(&mp_irq);
+
+ return 0;
+}
+
static int __init
acpi_parse_ioapic(struct acpi_subtable_header * header, const unsigned long end)
{
@@ -466,7 +477,11 @@
if (acpi_sci_flags & ACPI_MADT_POLARITY_MASK)
polarity = acpi_sci_flags & ACPI_MADT_POLARITY_MASK;
- mp_override_legacy_irq(bus_irq, polarity, trigger, gsi);
+ if (bus_irq < NR_IRQS_LEGACY)
+ mp_override_legacy_irq(bus_irq, polarity, trigger, gsi);
+ else
+ mp_register_ioapic_irq(bus_irq, polarity, trigger, gsi);
+
acpi_penalize_sci_irq(bus_irq, trigger, polarity);
/*
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index 5cb272a..10d5a3d 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -340,9 +340,12 @@
static void __init_or_module optimize_nops(struct alt_instr *a, u8 *instr)
{
unsigned long flags;
+ int i;
- if (instr[0] != 0x90)
- return;
+ for (i = 0; i < a->padlen; i++) {
+ if (instr[i] != 0x90)
+ return;
+ }
local_irq_save(flags);
add_nops(instr + (a->instrlen - a->padlen), a->padlen);
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile
index 4a8697f..33b6367 100644
--- a/arch/x86/kernel/cpu/Makefile
+++ b/arch/x86/kernel/cpu/Makefile
@@ -20,13 +20,11 @@
obj-y += common.o
obj-y += rdrand.o
obj-y += match.o
+obj-y += bugs.o
obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_X86_FEATURE_NAMES) += capflags.o powerflags.o
-obj-$(CONFIG_X86_32) += bugs.o
-obj-$(CONFIG_X86_64) += bugs_64.o
-
obj-$(CONFIG_CPU_SUP_INTEL) += intel.o
obj-$(CONFIG_CPU_SUP_AMD) += amd.o
obj-$(CONFIG_CPU_SUP_CYRIX_32) += cyrix.o
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index 2b4cf04..1b89f0c 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -782,8 +782,32 @@
set_cpu_cap(c, X86_FEATURE_K8);
if (cpu_has(c, X86_FEATURE_XMM2)) {
- /* MFENCE stops RDTSC speculation */
- set_cpu_cap(c, X86_FEATURE_MFENCE_RDTSC);
+ unsigned long long val;
+ int ret;
+
+ /*
+ * A serializing LFENCE has less overhead than MFENCE, so
+ * use it for execution serialization. On families which
+ * don't have that MSR, LFENCE is already serializing.
+ * msr_set_bit() uses the safe accessors, too, even if the MSR
+ * is not present.
+ */
+ msr_set_bit(MSR_F10H_DECFG,
+ MSR_F10H_DECFG_LFENCE_SERIALIZE_BIT);
+
+ /*
+ * Verify that the MSR write was successful (could be running
+ * under a hypervisor) and only then assume that LFENCE is
+ * serializing.
+ */
+ ret = rdmsrl_safe(MSR_F10H_DECFG, &val);
+ if (!ret && (val & MSR_F10H_DECFG_LFENCE_SERIALIZE)) {
+ /* A serializing LFENCE stops RDTSC speculation */
+ set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC);
+ } else {
+ /* MFENCE stops RDTSC speculation */
+ set_cpu_cap(c, X86_FEATURE_MFENCE_RDTSC);
+ }
}
/*
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
index bd17db1..49d25dd 100644
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -9,6 +9,10 @@
*/
#include <linux/init.h>
#include <linux/utsname.h>
+#include <linux/cpu.h>
+
+#include <asm/nospec-branch.h>
+#include <asm/cmdline.h>
#include <asm/bugs.h>
#include <asm/processor.h>
#include <asm/processor-flags.h>
@@ -16,15 +20,24 @@
#include <asm/msr.h>
#include <asm/paravirt.h>
#include <asm/alternative.h>
+#include <asm/pgtable.h>
+#include <asm/cacheflush.h>
+
+static void __init spectre_v2_select_mitigation(void);
void __init check_bugs(void)
{
identify_boot_cpu();
-#ifndef CONFIG_SMP
- pr_info("CPU: ");
- print_cpu_info(&boot_cpu_data);
-#endif
+ if (!IS_ENABLED(CONFIG_SMP)) {
+ pr_info("CPU: ");
+ print_cpu_info(&boot_cpu_data);
+ }
+
+ /* Select the proper spectre mitigation before patching alternatives */
+ spectre_v2_select_mitigation();
+
+#ifdef CONFIG_X86_32
/*
* Check whether we are able to run this kernel safely on SMP.
*
@@ -40,4 +53,194 @@
alternative_instructions();
fpu__init_check_bugs();
+#else /* CONFIG_X86_64 */
+ alternative_instructions();
+
+ /*
+ * Make sure the first 2MB area is not mapped by huge pages
+ * There are typically fixed size MTRRs in there and overlapping
+ * MTRRs into large pages causes slow downs.
+ *
+ * Right now we don't do that with gbpages because there seems
+ * very little benefit for that case.
+ */
+ if (!direct_gbpages)
+ set_memory_4k((unsigned long)__va(0), 1);
+#endif
}
+
+/* The kernel command line selection */
+enum spectre_v2_mitigation_cmd {
+ SPECTRE_V2_CMD_NONE,
+ SPECTRE_V2_CMD_AUTO,
+ SPECTRE_V2_CMD_FORCE,
+ SPECTRE_V2_CMD_RETPOLINE,
+ SPECTRE_V2_CMD_RETPOLINE_GENERIC,
+ SPECTRE_V2_CMD_RETPOLINE_AMD,
+};
+
+static const char *spectre_v2_strings[] = {
+ [SPECTRE_V2_NONE] = "Vulnerable",
+ [SPECTRE_V2_RETPOLINE_MINIMAL] = "Vulnerable: Minimal generic ASM retpoline",
+ [SPECTRE_V2_RETPOLINE_MINIMAL_AMD] = "Vulnerable: Minimal AMD ASM retpoline",
+ [SPECTRE_V2_RETPOLINE_GENERIC] = "Mitigation: Full generic retpoline",
+ [SPECTRE_V2_RETPOLINE_AMD] = "Mitigation: Full AMD retpoline",
+};
+
+#undef pr_fmt
+#define pr_fmt(fmt) "Spectre V2 mitigation: " fmt
+
+static enum spectre_v2_mitigation spectre_v2_enabled = SPECTRE_V2_NONE;
+
+static void __init spec2_print_if_insecure(const char *reason)
+{
+ if (boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
+ pr_info("%s\n", reason);
+}
+
+static void __init spec2_print_if_secure(const char *reason)
+{
+ if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
+ pr_info("%s\n", reason);
+}
+
+static inline bool retp_compiler(void)
+{
+ return __is_defined(RETPOLINE);
+}
+
+static inline bool match_option(const char *arg, int arglen, const char *opt)
+{
+ int len = strlen(opt);
+
+ return len == arglen && !strncmp(arg, opt, len);
+}
+
+static enum spectre_v2_mitigation_cmd __init spectre_v2_parse_cmdline(void)
+{
+ char arg[20];
+ int ret;
+
+ ret = cmdline_find_option(boot_command_line, "spectre_v2", arg,
+ sizeof(arg));
+ if (ret > 0) {
+ if (match_option(arg, ret, "off")) {
+ goto disable;
+ } else if (match_option(arg, ret, "on")) {
+ spec2_print_if_secure("force enabled on command line.");
+ return SPECTRE_V2_CMD_FORCE;
+ } else if (match_option(arg, ret, "retpoline")) {
+ spec2_print_if_insecure("retpoline selected on command line.");
+ return SPECTRE_V2_CMD_RETPOLINE;
+ } else if (match_option(arg, ret, "retpoline,amd")) {
+ if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) {
+ pr_err("retpoline,amd selected but CPU is not AMD. Switching to AUTO select\n");
+ return SPECTRE_V2_CMD_AUTO;
+ }
+ spec2_print_if_insecure("AMD retpoline selected on command line.");
+ return SPECTRE_V2_CMD_RETPOLINE_AMD;
+ } else if (match_option(arg, ret, "retpoline,generic")) {
+ spec2_print_if_insecure("generic retpoline selected on command line.");
+ return SPECTRE_V2_CMD_RETPOLINE_GENERIC;
+ } else if (match_option(arg, ret, "auto")) {
+ return SPECTRE_V2_CMD_AUTO;
+ }
+ }
+
+ if (!cmdline_find_option_bool(boot_command_line, "nospectre_v2"))
+ return SPECTRE_V2_CMD_AUTO;
+disable:
+ spec2_print_if_insecure("disabled on command line.");
+ return SPECTRE_V2_CMD_NONE;
+}
+
+static void __init spectre_v2_select_mitigation(void)
+{
+ enum spectre_v2_mitigation_cmd cmd = spectre_v2_parse_cmdline();
+ enum spectre_v2_mitigation mode = SPECTRE_V2_NONE;
+
+ /*
+ * If the CPU is not affected and the command line mode is NONE or AUTO
+ * then nothing to do.
+ */
+ if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2) &&
+ (cmd == SPECTRE_V2_CMD_NONE || cmd == SPECTRE_V2_CMD_AUTO))
+ return;
+
+ switch (cmd) {
+ case SPECTRE_V2_CMD_NONE:
+ return;
+
+ case SPECTRE_V2_CMD_FORCE:
+ /* FALLTRHU */
+ case SPECTRE_V2_CMD_AUTO:
+ goto retpoline_auto;
+
+ case SPECTRE_V2_CMD_RETPOLINE_AMD:
+ if (IS_ENABLED(CONFIG_RETPOLINE))
+ goto retpoline_amd;
+ break;
+ case SPECTRE_V2_CMD_RETPOLINE_GENERIC:
+ if (IS_ENABLED(CONFIG_RETPOLINE))
+ goto retpoline_generic;
+ break;
+ case SPECTRE_V2_CMD_RETPOLINE:
+ if (IS_ENABLED(CONFIG_RETPOLINE))
+ goto retpoline_auto;
+ break;
+ }
+ pr_err("kernel not compiled with retpoline; no mitigation available!");
+ return;
+
+retpoline_auto:
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) {
+ retpoline_amd:
+ if (!boot_cpu_has(X86_FEATURE_LFENCE_RDTSC)) {
+ pr_err("LFENCE not serializing. Switching to generic retpoline\n");
+ goto retpoline_generic;
+ }
+ mode = retp_compiler() ? SPECTRE_V2_RETPOLINE_AMD :
+ SPECTRE_V2_RETPOLINE_MINIMAL_AMD;
+ setup_force_cpu_cap(X86_FEATURE_RETPOLINE_AMD);
+ setup_force_cpu_cap(X86_FEATURE_RETPOLINE);
+ } else {
+ retpoline_generic:
+ mode = retp_compiler() ? SPECTRE_V2_RETPOLINE_GENERIC :
+ SPECTRE_V2_RETPOLINE_MINIMAL;
+ setup_force_cpu_cap(X86_FEATURE_RETPOLINE);
+ }
+
+ spectre_v2_enabled = mode;
+ pr_info("%s\n", spectre_v2_strings[mode]);
+}
+
+#undef pr_fmt
+
+#ifdef CONFIG_SYSFS
+ssize_t cpu_show_meltdown(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (!boot_cpu_has_bug(X86_BUG_CPU_MELTDOWN))
+ return sprintf(buf, "Not affected\n");
+ if (boot_cpu_has(X86_FEATURE_KAISER))
+ return sprintf(buf, "Mitigation: PTI\n");
+ return sprintf(buf, "Vulnerable\n");
+}
+
+ssize_t cpu_show_spectre_v1(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V1))
+ return sprintf(buf, "Not affected\n");
+ return sprintf(buf, "Vulnerable\n");
+}
+
+ssize_t cpu_show_spectre_v2(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (!boot_cpu_has_bug(X86_BUG_SPECTRE_V2))
+ return sprintf(buf, "Not affected\n");
+
+ return sprintf(buf, "%s\n", spectre_v2_strings[spectre_v2_enabled]);
+}
+#endif
diff --git a/arch/x86/kernel/cpu/bugs_64.c b/arch/x86/kernel/cpu/bugs_64.c
deleted file mode 100644
index a972ac4..0000000
--- a/arch/x86/kernel/cpu/bugs_64.c
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 1994 Linus Torvalds
- * Copyright (C) 2000 SuSE
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <asm/alternative.h>
-#include <asm/bugs.h>
-#include <asm/processor.h>
-#include <asm/mtrr.h>
-#include <asm/cacheflush.h>
-
-void __init check_bugs(void)
-{
- identify_boot_cpu();
-#if !defined(CONFIG_SMP)
- pr_info("CPU: ");
- print_cpu_info(&boot_cpu_data);
-#endif
- alternative_instructions();
-
- /*
- * Make sure the first 2MB area is not mapped by huge pages
- * There are typically fixed size MTRRs in there and overlapping
- * MTRRs into large pages causes slow downs.
- *
- * Right now we don't do that with gbpages because there seems
- * very little benefit for that case.
- */
- if (!direct_gbpages)
- set_memory_4k((unsigned long)__va(0), 1);
-}
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 4eece91..7b9ae04 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -93,7 +93,7 @@
static const struct cpu_dev *this_cpu = &default_cpu;
-DEFINE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page) = { .gdt = {
+DEFINE_PER_CPU_PAGE_ALIGNED_USER_MAPPED(struct gdt_page, gdt_page) = { .gdt = {
#ifdef CONFIG_X86_64
/*
* We need valid kernel segments for data and code in long mode too
@@ -163,6 +163,24 @@
}
__setup("nompx", x86_mpx_setup);
+#ifdef CONFIG_X86_64
+static int __init x86_pcid_setup(char *s)
+{
+ /* require an exact match without trailing characters */
+ if (strlen(s))
+ return 0;
+
+ /* do not emit a message if the feature is not present */
+ if (!boot_cpu_has(X86_FEATURE_PCID))
+ return 1;
+
+ setup_clear_cpu_cap(X86_FEATURE_PCID);
+ pr_info("nopcid: PCID feature disabled\n");
+ return 1;
+}
+__setup("nopcid", x86_pcid_setup);
+#endif
+
static int __init x86_noinvpcid_setup(char *s)
{
/* noinvpcid doesn't accept parameters */
@@ -306,6 +324,39 @@
}
}
+static void setup_pcid(struct cpuinfo_x86 *c)
+{
+ if (cpu_has(c, X86_FEATURE_PCID)) {
+ if (cpu_has(c, X86_FEATURE_PGE) || kaiser_enabled) {
+ cr4_set_bits(X86_CR4_PCIDE);
+ /*
+ * INVPCID has two "groups" of types:
+ * 1/2: Invalidate an individual address
+ * 3/4: Invalidate all contexts
+ *
+ * 1/2 take a PCID, but 3/4 do not. So, 3/4
+ * ignore the PCID argument in the descriptor.
+ * But, we have to be careful not to call 1/2
+ * with an actual non-zero PCID in them before
+ * we do the above cr4_set_bits().
+ */
+ if (cpu_has(c, X86_FEATURE_INVPCID))
+ set_cpu_cap(c, X86_FEATURE_INVPCID_SINGLE);
+ } else {
+ /*
+ * flush_tlb_all(), as currently implemented, won't
+ * work if PCID is on but PGE is not. Since that
+ * combination doesn't exist on real hardware, there's
+ * no reason to try to fully support it, but it's
+ * polite to avoid corrupting data if we're on
+ * an improperly configured VM.
+ */
+ clear_cpu_cap(c, X86_FEATURE_PCID);
+ }
+ }
+ kaiser_setup_pcid();
+}
+
/*
* Protection Keys are not available in 32-bit mode.
*/
@@ -429,8 +480,8 @@
return NULL; /* Not found */
}
-__u32 cpu_caps_cleared[NCAPINTS];
-__u32 cpu_caps_set[NCAPINTS];
+__u32 cpu_caps_cleared[NCAPINTS + NBUGINTS];
+__u32 cpu_caps_set[NCAPINTS + NBUGINTS];
void load_percpu_segment(int cpu)
{
@@ -655,6 +706,16 @@
}
}
+static void apply_forced_caps(struct cpuinfo_x86 *c)
+{
+ int i;
+
+ for (i = 0; i < NCAPINTS + NBUGINTS; i++) {
+ c->x86_capability[i] &= ~cpu_caps_cleared[i];
+ c->x86_capability[i] |= cpu_caps_set[i];
+ }
+}
+
void get_cpu_cap(struct cpuinfo_x86 *c)
{
u32 eax, ebx, ecx, edx;
@@ -821,7 +882,22 @@
}
setup_force_cpu_cap(X86_FEATURE_ALWAYS);
+
+ /* Assume for now that ALL x86 CPUs are insecure */
+ setup_force_cpu_bug(X86_BUG_CPU_MELTDOWN);
+
+ setup_force_cpu_bug(X86_BUG_SPECTRE_V1);
+ setup_force_cpu_bug(X86_BUG_SPECTRE_V2);
+
fpu__init_system(c);
+
+#ifdef CONFIG_X86_32
+ /*
+ * Regardless of whether PCID is enumerated, the SDM says
+ * that it can't be enabled in 32-bit mode.
+ */
+ setup_clear_cpu_cap(X86_FEATURE_PCID);
+#endif
}
void __init early_cpu_init(void)
@@ -1035,10 +1111,7 @@
this_cpu->c_identify(c);
/* Clear/Set all flags overridden by options, after probe */
- for (i = 0; i < NCAPINTS; i++) {
- c->x86_capability[i] &= ~cpu_caps_cleared[i];
- c->x86_capability[i] |= cpu_caps_set[i];
- }
+ apply_forced_caps(c);
#ifdef CONFIG_X86_64
c->apicid = apic->phys_pkg_id(c->initial_apicid, 0);
@@ -1064,6 +1137,9 @@
setup_smep(c);
setup_smap(c);
+ /* Set up PCID */
+ setup_pcid(c);
+
/*
* The vendor-specific functions might have changed features.
* Now we do "generic changes."
@@ -1097,10 +1173,7 @@
* Clear/Set all flags overridden by options, need do it
* before following smp all cpus cap AND.
*/
- for (i = 0; i < NCAPINTS; i++) {
- c->x86_capability[i] &= ~cpu_caps_cleared[i];
- c->x86_capability[i] |= cpu_caps_set[i];
- }
+ apply_forced_caps(c);
/*
* On SMP, boot_cpu_data holds the common feature set between
@@ -1325,7 +1398,7 @@
[DEBUG_STACK - 1] = DEBUG_STKSZ
};
-static DEFINE_PER_CPU_PAGE_ALIGNED(char, exception_stacks
+DEFINE_PER_CPU_PAGE_ALIGNED_USER_MAPPED(char, exception_stacks
[(N_EXCEPTION_STACKS - 1) * EXCEPTION_STKSZ + DEBUG_STKSZ]);
/* May not be marked __init: used by software suspend */
@@ -1483,6 +1556,14 @@
* try to read it.
*/
cr4_init_shadow();
+ if (!kaiser_enabled) {
+ /*
+ * secondary_startup_64() deferred setting PGE in cr4:
+ * probe_page_size_mask() sets it on the boot cpu,
+ * but it needs to be set on each secondary cpu.
+ */
+ cr4_set_bits(X86_CR4_PGE);
+ }
/*
* Load microcode on this cpu if a valid microcode is available.
diff --git a/arch/x86/kernel/cpu/microcode/amd.c b/arch/x86/kernel/cpu/microcode/amd.c
index 017bda1..b74bb29 100644
--- a/arch/x86/kernel/cpu/microcode/amd.c
+++ b/arch/x86/kernel/cpu/microcode/amd.c
@@ -592,6 +592,7 @@
#define F14H_MPB_MAX_SIZE 1824
#define F15H_MPB_MAX_SIZE 4096
#define F16H_MPB_MAX_SIZE 3458
+#define F17H_MPB_MAX_SIZE 3200
switch (family) {
case 0x14:
@@ -603,6 +604,9 @@
case 0x16:
max_size = F16H_MPB_MAX_SIZE;
break;
+ case 0x17:
+ max_size = F17H_MPB_MAX_SIZE;
+ break;
default:
max_size = F1XH_MPB_MAX_SIZE;
break;
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
index 13dbcc0..ac3e636 100644
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -1051,8 +1051,17 @@
{
struct cpuinfo_x86 *c = &cpu_data(cpu);
- if (c->x86 == 6 && c->x86_model == INTEL_FAM6_BROADWELL_X) {
- pr_err_once("late loading on model 79 is disabled.\n");
+ /*
+ * Late loading on model 79 with microcode revision less than 0x0b000021
+ * may result in a system hang. This behavior is documented in item
+ * BDF90, #334165 (Intel Xeon Processor E7-8800/4800 v4 Product Family).
+ */
+ if (c->x86 == 6 &&
+ c->x86_model == INTEL_FAM6_BROADWELL_X &&
+ c->x86_mask == 0x01 &&
+ c->microcode < 0x0b000021) {
+ pr_err_once("Erratum BDF90: late loading with revision < 0x0b000021 (0x%x) disabled.\n", c->microcode);
+ pr_err_once("Please consider either early loading through initrd/built-in or a potential BIOS update.\n");
return true;
}
diff --git a/arch/x86/kernel/espfix_64.c b/arch/x86/kernel/espfix_64.c
index 04f89ca..e33b385 100644
--- a/arch/x86/kernel/espfix_64.c
+++ b/arch/x86/kernel/espfix_64.c
@@ -41,6 +41,7 @@
#include <asm/pgalloc.h>
#include <asm/setup.h>
#include <asm/espfix.h>
+#include <asm/kaiser.h>
/*
* Note: we only need 6*8 = 48 bytes for the espfix stack, but round
@@ -126,6 +127,15 @@
/* Install the espfix pud into the kernel page directory */
pgd_p = &init_level4_pgt[pgd_index(ESPFIX_BASE_ADDR)];
pgd_populate(&init_mm, pgd_p, (pud_t *)espfix_pud_page);
+ /*
+ * Just copy the top-level PGD that is mapping the espfix
+ * area to ensure it is mapped into the shadow user page
+ * tables.
+ */
+ if (kaiser_enabled) {
+ set_pgd(native_get_shadow_pgd(pgd_p),
+ __pgd(_KERNPG_TABLE | __pa((pud_t *)espfix_pud_page)));
+ }
/* Randomize the locations */
init_espfix_random();
diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S
index b4421cc..67cd7c1 100644
--- a/arch/x86/kernel/head_64.S
+++ b/arch/x86/kernel/head_64.S
@@ -190,8 +190,8 @@
movq $(init_level4_pgt - __START_KERNEL_map), %rax
1:
- /* Enable PAE mode and PGE */
- movl $(X86_CR4_PAE | X86_CR4_PGE), %ecx
+ /* Enable PAE and PSE, but defer PGE until kaiser_enabled is decided */
+ movl $(X86_CR4_PAE | X86_CR4_PSE), %ecx
movq %rcx, %cr4
/* Setup early boot stage 4 level pagetables. */
@@ -405,6 +405,27 @@
.balign PAGE_SIZE; \
GLOBAL(name)
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+/*
+ * Each PGD needs to be 8k long and 8k aligned. We do not
+ * ever go out to userspace with these, so we do not
+ * strictly *need* the second page, but this allows us to
+ * have a single set_pgd() implementation that does not
+ * need to worry about whether it has 4k or 8k to work
+ * with.
+ *
+ * This ensures PGDs are 8k long:
+ */
+#define KAISER_USER_PGD_FILL 512
+/* This ensures they are 8k-aligned: */
+#define NEXT_PGD_PAGE(name) \
+ .balign 2 * PAGE_SIZE; \
+GLOBAL(name)
+#else
+#define NEXT_PGD_PAGE(name) NEXT_PAGE(name)
+#define KAISER_USER_PGD_FILL 0
+#endif
+
/* Automate the creation of 1 to 1 mapping pmd entries */
#define PMDS(START, PERM, COUNT) \
i = 0 ; \
@@ -414,9 +435,10 @@
.endr
__INITDATA
-NEXT_PAGE(early_level4_pgt)
+NEXT_PGD_PAGE(early_level4_pgt)
.fill 511,8,0
.quad level3_kernel_pgt - __START_KERNEL_map + _PAGE_TABLE
+ .fill KAISER_USER_PGD_FILL,8,0
NEXT_PAGE(early_dynamic_pgts)
.fill 512*EARLY_DYNAMIC_PAGE_TABLES,8,0
@@ -424,16 +446,18 @@
.data
#ifndef CONFIG_XEN
-NEXT_PAGE(init_level4_pgt)
+NEXT_PGD_PAGE(init_level4_pgt)
.fill 512,8,0
+ .fill KAISER_USER_PGD_FILL,8,0
#else
-NEXT_PAGE(init_level4_pgt)
+NEXT_PGD_PAGE(init_level4_pgt)
.quad level3_ident_pgt - __START_KERNEL_map + _KERNPG_TABLE
.org init_level4_pgt + L4_PAGE_OFFSET*8, 0
.quad level3_ident_pgt - __START_KERNEL_map + _KERNPG_TABLE
.org init_level4_pgt + L4_START_KERNEL*8, 0
/* (2^48-(2*1024*1024*1024))/(2^39) = 511 */
.quad level3_kernel_pgt - __START_KERNEL_map + _PAGE_TABLE
+ .fill KAISER_USER_PGD_FILL,8,0
NEXT_PAGE(level3_ident_pgt)
.quad level2_ident_pgt - __START_KERNEL_map + _KERNPG_TABLE
@@ -444,6 +468,7 @@
*/
PMDS(0, __PAGE_KERNEL_IDENT_LARGE_EXEC, PTRS_PER_PMD)
#endif
+ .fill KAISER_USER_PGD_FILL,8,0
NEXT_PAGE(level3_kernel_pgt)
.fill L3_START_KERNEL,8,0
diff --git a/arch/x86/kernel/irq_32.c b/arch/x86/kernel/irq_32.c
index 1f38d9a..2763573 100644
--- a/arch/x86/kernel/irq_32.c
+++ b/arch/x86/kernel/irq_32.c
@@ -19,6 +19,7 @@
#include <linux/mm.h>
#include <asm/apic.h>
+#include <asm/nospec-branch.h>
#ifdef CONFIG_DEBUG_STACKOVERFLOW
@@ -54,17 +55,17 @@
static void call_on_stack(void *func, void *stack)
{
asm volatile("xchgl %%ebx,%%esp \n"
- "call *%%edi \n"
+ CALL_NOSPEC
"movl %%ebx,%%esp \n"
: "=b" (stack)
: "0" (stack),
- "D"(func)
+ [thunk_target] "D"(func)
: "memory", "cc", "edx", "ecx", "eax");
}
static inline void *current_stack(void)
{
- return (void *)(current_stack_pointer() & ~(THREAD_SIZE - 1));
+ return (void *)(current_stack_pointer & ~(THREAD_SIZE - 1));
}
static inline int execute_on_irq_stack(int overflow, struct irq_desc *desc)
@@ -88,17 +89,17 @@
/* Save the next esp at the bottom of the stack */
prev_esp = (u32 *)irqstk;
- *prev_esp = current_stack_pointer();
+ *prev_esp = current_stack_pointer;
if (unlikely(overflow))
call_on_stack(print_stack_overflow, isp);
asm volatile("xchgl %%ebx,%%esp \n"
- "call *%%edi \n"
+ CALL_NOSPEC
"movl %%ebx,%%esp \n"
: "=a" (arg1), "=b" (isp)
: "0" (desc), "1" (isp),
- "D" (desc->handle_irq)
+ [thunk_target] "D" (desc->handle_irq)
: "memory", "cc", "ecx");
return 1;
}
@@ -139,7 +140,7 @@
/* Push the previous esp onto the stack */
prev_esp = (u32 *)irqstk;
- *prev_esp = current_stack_pointer();
+ *prev_esp = current_stack_pointer;
call_on_stack(__do_softirq, isp);
}
diff --git a/arch/x86/kernel/irqinit.c b/arch/x86/kernel/irqinit.c
index 1423ab1..f480b38 100644
--- a/arch/x86/kernel/irqinit.c
+++ b/arch/x86/kernel/irqinit.c
@@ -51,7 +51,7 @@
.flags = IRQF_NO_THREAD,
};
-DEFINE_PER_CPU(vector_irq_t, vector_irq) = {
+DEFINE_PER_CPU_USER_MAPPED(vector_irq_t, vector_irq) = {
[0 ... NR_VECTORS - 1] = VECTOR_UNUSED,
};
diff --git a/arch/x86/kernel/ldt.c b/arch/x86/kernel/ldt.c
index 5f70014..8bc68cf 100644
--- a/arch/x86/kernel/ldt.c
+++ b/arch/x86/kernel/ldt.c
@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/uaccess.h>
+#include <linux/kaiser.h>
#include <asm/ldt.h>
#include <asm/desc.h>
@@ -34,11 +35,21 @@
set_ldt(pc->ldt->entries, pc->ldt->size);
}
+static void __free_ldt_struct(struct ldt_struct *ldt)
+{
+ if (ldt->size * LDT_ENTRY_SIZE > PAGE_SIZE)
+ vfree(ldt->entries);
+ else
+ free_page((unsigned long)ldt->entries);
+ kfree(ldt);
+}
+
/* The caller must call finalize_ldt_struct on the result. LDT starts zeroed. */
static struct ldt_struct *alloc_ldt_struct(int size)
{
struct ldt_struct *new_ldt;
int alloc_size;
+ int ret;
if (size > LDT_ENTRIES)
return NULL;
@@ -66,7 +77,13 @@
return NULL;
}
+ ret = kaiser_add_mapping((unsigned long)new_ldt->entries, alloc_size,
+ __PAGE_KERNEL);
new_ldt->size = size;
+ if (ret) {
+ __free_ldt_struct(new_ldt);
+ return NULL;
+ }
return new_ldt;
}
@@ -92,12 +109,10 @@
if (likely(!ldt))
return;
+ kaiser_remove_mapping((unsigned long)ldt->entries,
+ ldt->size * LDT_ENTRY_SIZE);
paravirt_free_ldt(ldt->entries, ldt->size);
- if (ldt->size * LDT_ENTRY_SIZE > PAGE_SIZE)
- vfree(ldt->entries);
- else
- free_page((unsigned long)ldt->entries);
- kfree(ldt);
+ __free_ldt_struct(ldt);
}
/*
diff --git a/arch/x86/kernel/mcount_64.S b/arch/x86/kernel/mcount_64.S
index 7b0d3da..287ec3b 100644
--- a/arch/x86/kernel/mcount_64.S
+++ b/arch/x86/kernel/mcount_64.S
@@ -8,7 +8,7 @@
#include <asm/ptrace.h>
#include <asm/ftrace.h>
#include <asm/export.h>
-
+#include <asm/nospec-branch.h>
.code64
.section .entry.text, "ax"
@@ -290,8 +290,9 @@
* ip and parent ip are used and the list function is called when
* function tracing is enabled.
*/
- call *ftrace_trace_function
+ movq ftrace_trace_function, %r8
+ CALL_NOSPEC %r8
restore_mcount_regs
jmp fgraph_trace
@@ -334,5 +335,5 @@
movq 8(%rsp), %rdx
movq (%rsp), %rax
addq $24, %rsp
- jmp *%rdi
+ JMP_NOSPEC %rdi
#endif
diff --git a/arch/x86/kernel/paravirt_patch_64.c b/arch/x86/kernel/paravirt_patch_64.c
index bb3840c..ee43b36 100644
--- a/arch/x86/kernel/paravirt_patch_64.c
+++ b/arch/x86/kernel/paravirt_patch_64.c
@@ -9,7 +9,6 @@
DEF_NATIVE(pv_mmu_ops, read_cr2, "movq %cr2, %rax");
DEF_NATIVE(pv_mmu_ops, read_cr3, "movq %cr3, %rax");
DEF_NATIVE(pv_mmu_ops, write_cr3, "movq %rdi, %cr3");
-DEF_NATIVE(pv_mmu_ops, flush_tlb_single, "invlpg (%rdi)");
DEF_NATIVE(pv_cpu_ops, clts, "clts");
DEF_NATIVE(pv_cpu_ops, wbinvd, "wbinvd");
@@ -59,7 +58,6 @@
PATCH_SITE(pv_mmu_ops, read_cr3);
PATCH_SITE(pv_mmu_ops, write_cr3);
PATCH_SITE(pv_cpu_ops, clts);
- PATCH_SITE(pv_mmu_ops, flush_tlb_single);
PATCH_SITE(pv_cpu_ops, wbinvd);
#if defined(CONFIG_PARAVIRT_SPINLOCKS)
case PARAVIRT_PATCH(pv_lock_ops.queued_spin_unlock):
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index fc7cf64..54b2711 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -41,7 +41,7 @@
* section. Since TSS's are completely CPU-local, we want them
* on exact cacheline boundaries, to eliminate cacheline ping-pong.
*/
-__visible DEFINE_PER_CPU_SHARED_ALIGNED(struct tss_struct, cpu_tss) = {
+__visible DEFINE_PER_CPU_SHARED_ALIGNED_USER_MAPPED(struct tss_struct, cpu_tss) = {
.x86_tss = {
.sp0 = TOP_OF_INIT_STACK,
#ifdef CONFIG_X86_32
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index 067f981..ce020a6 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -106,6 +106,10 @@
load_cr3(initial_page_table);
#else
write_cr3(real_mode_header->trampoline_pgd);
+
+ /* Exiting long mode will fail if CR4.PCIDE is set. */
+ if (static_cpu_has(X86_FEATURE_PCID))
+ cr4_clear_bits(X86_CR4_PCIDE);
#endif
/* Jump to the identity-mapped low memory code */
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index feaab07..6b55012 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -114,6 +114,7 @@
#include <asm/microcode.h>
#include <asm/mmu_context.h>
#include <asm/kaslr.h>
+#include <asm/kaiser.h>
/*
* max_low_pfn_mapped: highest direct mapped pfn under 4GB
@@ -1019,6 +1020,12 @@
*/
init_hypervisor_platform();
+ /*
+ * This needs to happen right after XENPV is set on xen and
+ * kaiser_enabled is checked below in cleanup_highmap().
+ */
+ kaiser_check_boottime_disable();
+
x86_init.resources.probe_roms();
/* after parse_early_param, so could debug it */
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 9fe7b9e..e803d72 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -115,14 +115,10 @@
spin_lock_irqsave(&rtc_lock, flags);
CMOS_WRITE(0xa, 0xf);
spin_unlock_irqrestore(&rtc_lock, flags);
- local_flush_tlb();
- pr_debug("1.\n");
*((volatile unsigned short *)phys_to_virt(TRAMPOLINE_PHYS_HIGH)) =
start_eip >> 4;
- pr_debug("2.\n");
*((volatile unsigned short *)phys_to_virt(TRAMPOLINE_PHYS_LOW)) =
start_eip & 0xf;
- pr_debug("3.\n");
}
static inline void smpboot_restore_warm_reset_vector(void)
@@ -130,11 +126,6 @@
unsigned long flags;
/*
- * Install writable page 0 entry to set BIOS data area.
- */
- local_flush_tlb();
-
- /*
* Paranoid: Set warm reset code and vector here back
* to default values.
*/
diff --git a/arch/x86/kernel/tracepoint.c b/arch/x86/kernel/tracepoint.c
index 1c113db..2bb5ee4 100644
--- a/arch/x86/kernel/tracepoint.c
+++ b/arch/x86/kernel/tracepoint.c
@@ -9,10 +9,12 @@
#include <linux/atomic.h>
atomic_t trace_idt_ctr = ATOMIC_INIT(0);
+__aligned(PAGE_SIZE)
struct desc_ptr trace_idt_descr = { NR_VECTORS * 16 - 1,
(unsigned long) trace_idt_table };
/* No need to be aligned, but done to keep all IDTs defined the same way. */
+__aligned(PAGE_SIZE)
gate_desc trace_idt_table[NR_VECTORS] __page_aligned_bss;
static int trace_irq_vector_refcount;
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index bd4e3d4..322f433 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -153,7 +153,7 @@
* from double_fault.
*/
BUG_ON((unsigned long)(current_top_of_stack() -
- current_stack_pointer()) >= THREAD_SIZE);
+ current_stack_pointer) >= THREAD_SIZE);
preempt_enable_no_resched();
}
diff --git a/arch/x86/kernel/vm86_32.c b/arch/x86/kernel/vm86_32.c
index 01f30e5..4b30128 100644
--- a/arch/x86/kernel/vm86_32.c
+++ b/arch/x86/kernel/vm86_32.c
@@ -191,7 +191,7 @@
pte_unmap_unlock(pte, ptl);
out:
up_write(&mm->mmap_sem);
- flush_tlb();
+ flush_tlb_mm_range(mm, 0xA0000, 0xA0000 + 32*PAGE_SIZE, 0UL);
}
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 8148d8c..24af898 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -44,6 +44,7 @@
#include <asm/debugreg.h>
#include <asm/kvm_para.h>
#include <asm/irq_remapping.h>
+#include <asm/nospec-branch.h>
#include <asm/virtext.h>
#include "trace.h"
@@ -4869,6 +4870,25 @@
"mov %%r14, %c[r14](%[svm]) \n\t"
"mov %%r15, %c[r15](%[svm]) \n\t"
#endif
+ /*
+ * Clear host registers marked as clobbered to prevent
+ * speculative use.
+ */
+ "xor %%" _ASM_BX ", %%" _ASM_BX " \n\t"
+ "xor %%" _ASM_CX ", %%" _ASM_CX " \n\t"
+ "xor %%" _ASM_DX ", %%" _ASM_DX " \n\t"
+ "xor %%" _ASM_SI ", %%" _ASM_SI " \n\t"
+ "xor %%" _ASM_DI ", %%" _ASM_DI " \n\t"
+#ifdef CONFIG_X86_64
+ "xor %%r8, %%r8 \n\t"
+ "xor %%r9, %%r9 \n\t"
+ "xor %%r10, %%r10 \n\t"
+ "xor %%r11, %%r11 \n\t"
+ "xor %%r12, %%r12 \n\t"
+ "xor %%r13, %%r13 \n\t"
+ "xor %%r14, %%r14 \n\t"
+ "xor %%r15, %%r15 \n\t"
+#endif
"pop %%" _ASM_BP
:
: [svm]"a"(svm),
@@ -4898,6 +4918,9 @@
#endif
);
+ /* Eliminate branch target predictions from guest mode */
+ vmexit_fill_RSB();
+
#ifdef CONFIG_X86_64
wrmsrl(MSR_GS_BASE, svm->host.gs_base);
#else
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 263e560..3ca6d15 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -48,6 +48,7 @@
#include <asm/kexec.h>
#include <asm/apic.h>
#include <asm/irq_remapping.h>
+#include <asm/nospec-branch.h>
#include "trace.h"
#include "pmu.h"
@@ -857,8 +858,16 @@
{
BUILD_BUG_ON(ARRAY_SIZE(vmcs_field_to_offset_table) > SHRT_MAX);
- if (field >= ARRAY_SIZE(vmcs_field_to_offset_table) ||
- vmcs_field_to_offset_table[field] == 0)
+ if (field >= ARRAY_SIZE(vmcs_field_to_offset_table))
+ return -ENOENT;
+
+ /*
+ * FIXME: Mitigation for CVE-2017-5753. To be replaced with a
+ * generic mechanism.
+ */
+ asm("lfence");
+
+ if (vmcs_field_to_offset_table[field] == 0)
return -ENOENT;
return vmcs_field_to_offset_table[field];
@@ -8948,6 +8957,7 @@
/* Save guest registers, load host registers, keep flags */
"mov %0, %c[wordsize](%%" _ASM_SP ") \n\t"
"pop %0 \n\t"
+ "setbe %c[fail](%0)\n\t"
"mov %%" _ASM_AX ", %c[rax](%0) \n\t"
"mov %%" _ASM_BX ", %c[rbx](%0) \n\t"
__ASM_SIZE(pop) " %c[rcx](%0) \n\t"
@@ -8964,12 +8974,23 @@
"mov %%r13, %c[r13](%0) \n\t"
"mov %%r14, %c[r14](%0) \n\t"
"mov %%r15, %c[r15](%0) \n\t"
+ "xor %%r8d, %%r8d \n\t"
+ "xor %%r9d, %%r9d \n\t"
+ "xor %%r10d, %%r10d \n\t"
+ "xor %%r11d, %%r11d \n\t"
+ "xor %%r12d, %%r12d \n\t"
+ "xor %%r13d, %%r13d \n\t"
+ "xor %%r14d, %%r14d \n\t"
+ "xor %%r15d, %%r15d \n\t"
#endif
"mov %%cr2, %%" _ASM_AX " \n\t"
"mov %%" _ASM_AX ", %c[cr2](%0) \n\t"
+ "xor %%eax, %%eax \n\t"
+ "xor %%ebx, %%ebx \n\t"
+ "xor %%esi, %%esi \n\t"
+ "xor %%edi, %%edi \n\t"
"pop %%" _ASM_BP "; pop %%" _ASM_DX " \n\t"
- "setbe %c[fail](%0) \n\t"
".pushsection .rodata \n\t"
".global vmx_return \n\t"
"vmx_return: " _ASM_PTR " 2b \n\t"
@@ -9006,6 +9027,9 @@
#endif
);
+ /* Eliminate branch target predictions from guest mode */
+ vmexit_fill_RSB();
+
/* MSR_IA32_DEBUGCTLMSR is zeroed on vmexit. Restore it if needed */
if (debugctlmsr)
update_debugctlmsr(debugctlmsr);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 7e28e6c..d3f80cc 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -773,7 +773,8 @@
return 1;
/* PCID can not be enabled when cr3[11:0]!=000H or EFER.LMA=0 */
- if ((kvm_read_cr3(vcpu) & X86_CR3_PCID_MASK) || !is_long_mode(vcpu))
+ if ((kvm_read_cr3(vcpu) & X86_CR3_PCID_ASID_MASK) ||
+ !is_long_mode(vcpu))
return 1;
}
@@ -4263,7 +4264,7 @@
addr, n, v))
&& kvm_io_bus_read(vcpu, KVM_MMIO_BUS, addr, n, v))
break;
- trace_kvm_mmio(KVM_TRACE_MMIO_READ, n, addr, *(u64 *)v);
+ trace_kvm_mmio(KVM_TRACE_MMIO_READ, n, addr, v);
handled += n;
addr += n;
len -= n;
@@ -4516,7 +4517,7 @@
{
if (vcpu->mmio_read_completed) {
trace_kvm_mmio(KVM_TRACE_MMIO_READ, bytes,
- vcpu->mmio_fragments[0].gpa, *(u64 *)val);
+ vcpu->mmio_fragments[0].gpa, val);
vcpu->mmio_read_completed = 0;
return 1;
}
@@ -4538,14 +4539,14 @@
static int write_mmio(struct kvm_vcpu *vcpu, gpa_t gpa, int bytes, void *val)
{
- trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, *(u64 *)val);
+ trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, val);
return vcpu_mmio_write(vcpu, gpa, bytes, val);
}
static int read_exit_mmio(struct kvm_vcpu *vcpu, gpa_t gpa,
void *val, int bytes)
{
- trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, bytes, gpa, 0);
+ trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, bytes, gpa, NULL);
return X86EMUL_IO_NEEDED;
}
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index 34a7413..6bf1898 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -25,6 +25,7 @@
lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
lib-$(CONFIG_INSTRUCTION_DECODER) += insn.o inat.o
lib-$(CONFIG_RANDOMIZE_BASE) += kaslr.o
+lib-$(CONFIG_RETPOLINE) += retpoline.o
obj-y += msr.o msr-reg.o msr-reg-export.o hweight.o
diff --git a/arch/x86/lib/checksum_32.S b/arch/x86/lib/checksum_32.S
index 4d34bb5..46e71a7 100644
--- a/arch/x86/lib/checksum_32.S
+++ b/arch/x86/lib/checksum_32.S
@@ -29,7 +29,8 @@
#include <asm/errno.h>
#include <asm/asm.h>
#include <asm/export.h>
-
+#include <asm/nospec-branch.h>
+
/*
* computes a partial checksum, e.g. for TCP/UDP fragments
*/
@@ -156,7 +157,7 @@
negl %ebx
lea 45f(%ebx,%ebx,2), %ebx
testl %esi, %esi
- jmp *%ebx
+ JMP_NOSPEC %ebx
# Handle 2-byte-aligned regions
20: addw (%esi), %ax
@@ -439,7 +440,7 @@
andl $-32,%edx
lea 3f(%ebx,%ebx), %ebx
testl %esi, %esi
- jmp *%ebx
+ JMP_NOSPEC %ebx
1: addl $64,%esi
addl $64,%edi
SRC(movb -32(%edx),%bl) ; SRC(movb (%edx),%bl)
diff --git a/arch/x86/lib/cmdline.c b/arch/x86/lib/cmdline.c
index 5cc78bf..3261abb 100644
--- a/arch/x86/lib/cmdline.c
+++ b/arch/x86/lib/cmdline.c
@@ -104,7 +104,112 @@
return 0; /* Buffer overrun */
}
+/*
+ * Find a non-boolean option (i.e. option=argument). In accordance with
+ * standard Linux practice, if this option is repeated, this returns the
+ * last instance on the command line.
+ *
+ * @cmdline: the cmdline string
+ * @max_cmdline_size: the maximum size of cmdline
+ * @option: option string to look for
+ * @buffer: memory buffer to return the option argument
+ * @bufsize: size of the supplied memory buffer
+ *
+ * Returns the length of the argument (regardless of if it was
+ * truncated to fit in the buffer), or -1 on not found.
+ */
+static int
+__cmdline_find_option(const char *cmdline, int max_cmdline_size,
+ const char *option, char *buffer, int bufsize)
+{
+ char c;
+ int pos = 0, len = -1;
+ const char *opptr = NULL;
+ char *bufptr = buffer;
+ enum {
+ st_wordstart = 0, /* Start of word/after whitespace */
+ st_wordcmp, /* Comparing this word */
+ st_wordskip, /* Miscompare, skip */
+ st_bufcpy, /* Copying this to buffer */
+ } state = st_wordstart;
+
+ if (!cmdline)
+ return -1; /* No command line */
+
+ /*
+ * This 'pos' check ensures we do not overrun
+ * a non-NULL-terminated 'cmdline'
+ */
+ while (pos++ < max_cmdline_size) {
+ c = *(char *)cmdline++;
+ if (!c)
+ break;
+
+ switch (state) {
+ case st_wordstart:
+ if (myisspace(c))
+ break;
+
+ state = st_wordcmp;
+ opptr = option;
+ /* fall through */
+
+ case st_wordcmp:
+ if ((c == '=') && !*opptr) {
+ /*
+ * We matched all the way to the end of the
+ * option we were looking for, prepare to
+ * copy the argument.
+ */
+ len = 0;
+ bufptr = buffer;
+ state = st_bufcpy;
+ break;
+ } else if (c == *opptr++) {
+ /*
+ * We are currently matching, so continue
+ * to the next character on the cmdline.
+ */
+ break;
+ }
+ state = st_wordskip;
+ /* fall through */
+
+ case st_wordskip:
+ if (myisspace(c))
+ state = st_wordstart;
+ break;
+
+ case st_bufcpy:
+ if (myisspace(c)) {
+ state = st_wordstart;
+ } else {
+ /*
+ * Increment len, but don't overrun the
+ * supplied buffer and leave room for the
+ * NULL terminator.
+ */
+ if (++len < bufsize)
+ *bufptr++ = c;
+ }
+ break;
+ }
+ }
+
+ if (bufsize)
+ *bufptr = '\0';
+
+ return len;
+}
+
int cmdline_find_option_bool(const char *cmdline, const char *option)
{
return __cmdline_find_option_bool(cmdline, COMMAND_LINE_SIZE, option);
}
+
+int cmdline_find_option(const char *cmdline, const char *option, char *buffer,
+ int bufsize)
+{
+ return __cmdline_find_option(cmdline, COMMAND_LINE_SIZE, option,
+ buffer, bufsize);
+}
diff --git a/arch/x86/lib/retpoline.S b/arch/x86/lib/retpoline.S
new file mode 100644
index 0000000..cb45c6c
--- /dev/null
+++ b/arch/x86/lib/retpoline.S
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <linux/stringify.h>
+#include <linux/linkage.h>
+#include <asm/dwarf2.h>
+#include <asm/cpufeatures.h>
+#include <asm/alternative-asm.h>
+#include <asm/export.h>
+#include <asm/nospec-branch.h>
+
+.macro THUNK reg
+ .section .text.__x86.indirect_thunk.\reg
+
+ENTRY(__x86_indirect_thunk_\reg)
+ CFI_STARTPROC
+ JMP_NOSPEC %\reg
+ CFI_ENDPROC
+ENDPROC(__x86_indirect_thunk_\reg)
+.endm
+
+/*
+ * Despite being an assembler file we can't just use .irp here
+ * because __KSYM_DEPS__ only uses the C preprocessor and would
+ * only see one instance of "__x86_indirect_thunk_\reg" rather
+ * than one per register with the correct names. So we do it
+ * the simple and nasty way...
+ */
+#define EXPORT_THUNK(reg) EXPORT_SYMBOL(__x86_indirect_thunk_ ## reg)
+#define GENERATE_THUNK(reg) THUNK reg ; EXPORT_THUNK(reg)
+
+GENERATE_THUNK(_ASM_AX)
+GENERATE_THUNK(_ASM_BX)
+GENERATE_THUNK(_ASM_CX)
+GENERATE_THUNK(_ASM_DX)
+GENERATE_THUNK(_ASM_SI)
+GENERATE_THUNK(_ASM_DI)
+GENERATE_THUNK(_ASM_BP)
+GENERATE_THUNK(_ASM_SP)
+#ifdef CONFIG_64BIT
+GENERATE_THUNK(r8)
+GENERATE_THUNK(r9)
+GENERATE_THUNK(r10)
+GENERATE_THUNK(r11)
+GENERATE_THUNK(r12)
+GENERATE_THUNK(r13)
+GENERATE_THUNK(r14)
+GENERATE_THUNK(r15)
+#endif
diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile
index 96d2b84..c548b46 100644
--- a/arch/x86/mm/Makefile
+++ b/arch/x86/mm/Makefile
@@ -37,5 +37,5 @@
obj-$(CONFIG_X86_INTEL_MPX) += mpx.o
obj-$(CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS) += pkeys.o
-obj-$(CONFIG_RANDOMIZE_MEMORY) += kaslr.o
-
+obj-$(CONFIG_RANDOMIZE_MEMORY) += kaslr.o
+obj-$(CONFIG_PAGE_TABLE_ISOLATION) += kaiser.o
diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c
index 889e761..f92bdb9 100644
--- a/arch/x86/mm/init.c
+++ b/arch/x86/mm/init.c
@@ -177,7 +177,7 @@
cr4_set_bits_and_update_boot(X86_CR4_PSE);
/* Enable PGE if available */
- if (boot_cpu_has(X86_FEATURE_PGE)) {
+ if (boot_cpu_has(X86_FEATURE_PGE) && !kaiser_enabled) {
cr4_set_bits_and_update_boot(X86_CR4_PGE);
__supported_pte_mask |= _PAGE_GLOBAL;
} else
@@ -764,13 +764,11 @@
}
DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate) = {
-#ifdef CONFIG_SMP
.active_mm = &init_mm,
.state = 0,
-#endif
.cr4 = ~0UL, /* fail hard if we screw up cr4 shadow initialization */
};
-EXPORT_SYMBOL_GPL(cpu_tlbstate);
+EXPORT_PER_CPU_SYMBOL(cpu_tlbstate);
void update_cache_mode_entry(unsigned entry, enum page_cache_mode cache)
{
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index 3e27ded..7df8e3a 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -324,6 +324,16 @@
continue;
if (vaddr < (unsigned long) _text || vaddr > end)
set_pmd(pmd, __pmd(0));
+ else if (kaiser_enabled) {
+ /*
+ * level2_kernel_pgt is initialized with _PAGE_GLOBAL:
+ * clear that now. This is not important, so long as
+ * CR4.PGE remains clear, but it removes an anomaly.
+ * Physical mapping setup below avoids _PAGE_GLOBAL
+ * by use of massage_pgprot() inside pfn_pte() etc.
+ */
+ set_pmd(pmd, pmd_clear_flags(*pmd, _PAGE_GLOBAL));
+ }
}
}
diff --git a/arch/x86/mm/kaiser.c b/arch/x86/mm/kaiser.c
new file mode 100644
index 0000000..a8ade08
--- /dev/null
+++ b/arch/x86/mm/kaiser.c
@@ -0,0 +1,483 @@
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/bug.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/uaccess.h>
+
+#undef pr_fmt
+#define pr_fmt(fmt) "Kernel/User page tables isolation: " fmt
+
+#include <asm/kaiser.h>
+#include <asm/tlbflush.h> /* to verify its kaiser declarations */
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/desc.h>
+#include <asm/cmdline.h>
+#include <asm/vsyscall.h>
+
+int kaiser_enabled __read_mostly = 1;
+EXPORT_SYMBOL(kaiser_enabled); /* for inlined TLB flush functions */
+
+__visible
+DEFINE_PER_CPU_USER_MAPPED(unsigned long, unsafe_stack_register_backup);
+
+/*
+ * These can have bit 63 set, so we can not just use a plain "or"
+ * instruction to get their value or'd into CR3. It would take
+ * another register. So, we use a memory reference to these instead.
+ *
+ * This is also handy because systems that do not support PCIDs
+ * just end up or'ing a 0 into their CR3, which does no harm.
+ */
+DEFINE_PER_CPU(unsigned long, x86_cr3_pcid_user);
+
+/*
+ * At runtime, the only things we map are some things for CPU
+ * hotplug, and stacks for new processes. No two CPUs will ever
+ * be populating the same addresses, so we only need to ensure
+ * that we protect between two CPUs trying to allocate and
+ * populate the same page table page.
+ *
+ * Only take this lock when doing a set_p[4um]d(), but it is not
+ * needed for doing a set_pte(). We assume that only the *owner*
+ * of a given allocation will be doing this for _their_
+ * allocation.
+ *
+ * This ensures that once a system has been running for a while
+ * and there have been stacks all over and these page tables
+ * are fully populated, there will be no further acquisitions of
+ * this lock.
+ */
+static DEFINE_SPINLOCK(shadow_table_allocation_lock);
+
+/*
+ * Returns -1 on error.
+ */
+static inline unsigned long get_pa_from_mapping(unsigned long vaddr)
+{
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ pgd = pgd_offset_k(vaddr);
+ /*
+ * We made all the kernel PGDs present in kaiser_init().
+ * We expect them to stay that way.
+ */
+ BUG_ON(pgd_none(*pgd));
+ /*
+ * PGDs are either 512GB or 128TB on all x86_64
+ * configurations. We don't handle these.
+ */
+ BUG_ON(pgd_large(*pgd));
+
+ pud = pud_offset(pgd, vaddr);
+ if (pud_none(*pud)) {
+ WARN_ON_ONCE(1);
+ return -1;
+ }
+
+ if (pud_large(*pud))
+ return (pud_pfn(*pud) << PAGE_SHIFT) | (vaddr & ~PUD_PAGE_MASK);
+
+ pmd = pmd_offset(pud, vaddr);
+ if (pmd_none(*pmd)) {
+ WARN_ON_ONCE(1);
+ return -1;
+ }
+
+ if (pmd_large(*pmd))
+ return (pmd_pfn(*pmd) << PAGE_SHIFT) | (vaddr & ~PMD_PAGE_MASK);
+
+ pte = pte_offset_kernel(pmd, vaddr);
+ if (pte_none(*pte)) {
+ WARN_ON_ONCE(1);
+ return -1;
+ }
+
+ return (pte_pfn(*pte) << PAGE_SHIFT) | (vaddr & ~PAGE_MASK);
+}
+
+/*
+ * This is a relatively normal page table walk, except that it
+ * also tries to allocate page tables pages along the way.
+ *
+ * Returns a pointer to a PTE on success, or NULL on failure.
+ */
+static pte_t *kaiser_pagetable_walk(unsigned long address, bool user)
+{
+ pmd_t *pmd;
+ pud_t *pud;
+ pgd_t *pgd = native_get_shadow_pgd(pgd_offset_k(address));
+ gfp_t gfp = (GFP_KERNEL | __GFP_NOTRACK | __GFP_ZERO);
+ unsigned long prot = _KERNPG_TABLE;
+
+ if (pgd_none(*pgd)) {
+ WARN_ONCE(1, "All shadow pgds should have been populated");
+ return NULL;
+ }
+ BUILD_BUG_ON(pgd_large(*pgd) != 0);
+
+ if (user) {
+ /*
+ * The vsyscall page is the only page that will have
+ * _PAGE_USER set. Catch everything else.
+ */
+ BUG_ON(address != VSYSCALL_ADDR);
+
+ set_pgd(pgd, __pgd(pgd_val(*pgd) | _PAGE_USER));
+ prot = _PAGE_TABLE;
+ }
+
+ pud = pud_offset(pgd, address);
+ /* The shadow page tables do not use large mappings: */
+ if (pud_large(*pud)) {
+ WARN_ON(1);
+ return NULL;
+ }
+ if (pud_none(*pud)) {
+ unsigned long new_pmd_page = __get_free_page(gfp);
+ if (!new_pmd_page)
+ return NULL;
+ spin_lock(&shadow_table_allocation_lock);
+ if (pud_none(*pud)) {
+ set_pud(pud, __pud(prot | __pa(new_pmd_page)));
+ __inc_zone_page_state(virt_to_page((void *)
+ new_pmd_page), NR_KAISERTABLE);
+ } else
+ free_page(new_pmd_page);
+ spin_unlock(&shadow_table_allocation_lock);
+ }
+
+ pmd = pmd_offset(pud, address);
+ /* The shadow page tables do not use large mappings: */
+ if (pmd_large(*pmd)) {
+ WARN_ON(1);
+ return NULL;
+ }
+ if (pmd_none(*pmd)) {
+ unsigned long new_pte_page = __get_free_page(gfp);
+ if (!new_pte_page)
+ return NULL;
+ spin_lock(&shadow_table_allocation_lock);
+ if (pmd_none(*pmd)) {
+ set_pmd(pmd, __pmd(prot | __pa(new_pte_page)));
+ __inc_zone_page_state(virt_to_page((void *)
+ new_pte_page), NR_KAISERTABLE);
+ } else
+ free_page(new_pte_page);
+ spin_unlock(&shadow_table_allocation_lock);
+ }
+
+ return pte_offset_kernel(pmd, address);
+}
+
+static int kaiser_add_user_map(const void *__start_addr, unsigned long size,
+ unsigned long flags)
+{
+ int ret = 0;
+ pte_t *pte;
+ unsigned long start_addr = (unsigned long )__start_addr;
+ unsigned long address = start_addr & PAGE_MASK;
+ unsigned long end_addr = PAGE_ALIGN(start_addr + size);
+ unsigned long target_address;
+
+ /*
+ * It is convenient for callers to pass in __PAGE_KERNEL etc,
+ * and there is no actual harm from setting _PAGE_GLOBAL, so
+ * long as CR4.PGE is not set. But it is nonetheless troubling
+ * to see Kaiser itself setting _PAGE_GLOBAL (now that "nokaiser"
+ * requires that not to be #defined to 0): so mask it off here.
+ */
+ flags &= ~_PAGE_GLOBAL;
+ if (!(__supported_pte_mask & _PAGE_NX))
+ flags &= ~_PAGE_NX;
+
+ for (; address < end_addr; address += PAGE_SIZE) {
+ target_address = get_pa_from_mapping(address);
+ if (target_address == -1) {
+ ret = -EIO;
+ break;
+ }
+ pte = kaiser_pagetable_walk(address, flags & _PAGE_USER);
+ if (!pte) {
+ ret = -ENOMEM;
+ break;
+ }
+ if (pte_none(*pte)) {
+ set_pte(pte, __pte(flags | target_address));
+ } else {
+ pte_t tmp;
+ set_pte(&tmp, __pte(flags | target_address));
+ WARN_ON_ONCE(!pte_same(*pte, tmp));
+ }
+ }
+ return ret;
+}
+
+static int kaiser_add_user_map_ptrs(const void *start, const void *end, unsigned long flags)
+{
+ unsigned long size = end - start;
+
+ return kaiser_add_user_map(start, size, flags);
+}
+
+/*
+ * Ensure that the top level of the (shadow) page tables are
+ * entirely populated. This ensures that all processes that get
+ * forked have the same entries. This way, we do not have to
+ * ever go set up new entries in older processes.
+ *
+ * Note: we never free these, so there are no updates to them
+ * after this.
+ */
+static void __init kaiser_init_all_pgds(void)
+{
+ pgd_t *pgd;
+ int i = 0;
+
+ pgd = native_get_shadow_pgd(pgd_offset_k((unsigned long )0));
+ for (i = PTRS_PER_PGD / 2; i < PTRS_PER_PGD; i++) {
+ pgd_t new_pgd;
+ pud_t *pud = pud_alloc_one(&init_mm,
+ PAGE_OFFSET + i * PGDIR_SIZE);
+ if (!pud) {
+ WARN_ON(1);
+ break;
+ }
+ inc_zone_page_state(virt_to_page(pud), NR_KAISERTABLE);
+ new_pgd = __pgd(_KERNPG_TABLE |__pa(pud));
+ /*
+ * Make sure not to stomp on some other pgd entry.
+ */
+ if (!pgd_none(pgd[i])) {
+ WARN_ON(1);
+ continue;
+ }
+ set_pgd(pgd + i, new_pgd);
+ }
+}
+
+#define kaiser_add_user_map_early(start, size, flags) do { \
+ int __ret = kaiser_add_user_map(start, size, flags); \
+ WARN_ON(__ret); \
+} while (0)
+
+#define kaiser_add_user_map_ptrs_early(start, end, flags) do { \
+ int __ret = kaiser_add_user_map_ptrs(start, end, flags); \
+ WARN_ON(__ret); \
+} while (0)
+
+void __init kaiser_check_boottime_disable(void)
+{
+ bool enable = true;
+ char arg[5];
+ int ret;
+
+ if (boot_cpu_has(X86_FEATURE_XENPV))
+ goto silent_disable;
+
+ ret = cmdline_find_option(boot_command_line, "pti", arg, sizeof(arg));
+ if (ret > 0) {
+ if (!strncmp(arg, "on", 2))
+ goto enable;
+
+ if (!strncmp(arg, "off", 3))
+ goto disable;
+
+ if (!strncmp(arg, "auto", 4))
+ goto skip;
+ }
+
+ if (cmdline_find_option_bool(boot_command_line, "nopti"))
+ goto disable;
+
+skip:
+ if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
+ goto disable;
+
+enable:
+ if (enable)
+ setup_force_cpu_cap(X86_FEATURE_KAISER);
+
+ return;
+
+disable:
+ pr_info("disabled\n");
+
+silent_disable:
+ kaiser_enabled = 0;
+ setup_clear_cpu_cap(X86_FEATURE_KAISER);
+}
+
+/*
+ * If anything in here fails, we will likely die on one of the
+ * first kernel->user transitions and init will die. But, we
+ * will have most of the kernel up by then and should be able to
+ * get a clean warning out of it. If we BUG_ON() here, we run
+ * the risk of being before we have good console output.
+ */
+void __init kaiser_init(void)
+{
+ int cpu;
+
+ if (!kaiser_enabled)
+ return;
+
+ kaiser_init_all_pgds();
+
+ /*
+ * Note that this sets _PAGE_USER and it needs to happen when the
+ * pagetable hierarchy gets created, i.e., early. Otherwise
+ * kaiser_pagetable_walk() will encounter initialized PTEs in the
+ * hierarchy and not set the proper permissions, leading to the
+ * pagefaults with page-protection violations when trying to read the
+ * vsyscall page. For example.
+ */
+ if (vsyscall_enabled())
+ kaiser_add_user_map_early((void *)VSYSCALL_ADDR,
+ PAGE_SIZE,
+ __PAGE_KERNEL_VSYSCALL);
+
+ for_each_possible_cpu(cpu) {
+ void *percpu_vaddr = __per_cpu_user_mapped_start +
+ per_cpu_offset(cpu);
+ unsigned long percpu_sz = __per_cpu_user_mapped_end -
+ __per_cpu_user_mapped_start;
+ kaiser_add_user_map_early(percpu_vaddr, percpu_sz,
+ __PAGE_KERNEL);
+ }
+
+ /*
+ * Map the entry/exit text section, which is needed at
+ * switches from user to and from kernel.
+ */
+ kaiser_add_user_map_ptrs_early(__entry_text_start, __entry_text_end,
+ __PAGE_KERNEL_RX);
+
+#if defined(CONFIG_FUNCTION_GRAPH_TRACER) || defined(CONFIG_KASAN)
+ kaiser_add_user_map_ptrs_early(__irqentry_text_start,
+ __irqentry_text_end,
+ __PAGE_KERNEL_RX);
+#endif
+ kaiser_add_user_map_early((void *)idt_descr.address,
+ sizeof(gate_desc) * NR_VECTORS,
+ __PAGE_KERNEL_RO);
+#ifdef CONFIG_TRACING
+ kaiser_add_user_map_early(&trace_idt_descr,
+ sizeof(trace_idt_descr),
+ __PAGE_KERNEL);
+ kaiser_add_user_map_early(&trace_idt_table,
+ sizeof(gate_desc) * NR_VECTORS,
+ __PAGE_KERNEL);
+#endif
+ kaiser_add_user_map_early(&debug_idt_descr, sizeof(debug_idt_descr),
+ __PAGE_KERNEL);
+ kaiser_add_user_map_early(&debug_idt_table,
+ sizeof(gate_desc) * NR_VECTORS,
+ __PAGE_KERNEL);
+
+ pr_info("enabled\n");
+}
+
+/* Add a mapping to the shadow mapping, and synchronize the mappings */
+int kaiser_add_mapping(unsigned long addr, unsigned long size, unsigned long flags)
+{
+ if (!kaiser_enabled)
+ return 0;
+ return kaiser_add_user_map((const void *)addr, size, flags);
+}
+
+void kaiser_remove_mapping(unsigned long start, unsigned long size)
+{
+ extern void unmap_pud_range_nofree(pgd_t *pgd,
+ unsigned long start, unsigned long end);
+ unsigned long end = start + size;
+ unsigned long addr, next;
+ pgd_t *pgd;
+
+ if (!kaiser_enabled)
+ return;
+ pgd = native_get_shadow_pgd(pgd_offset_k(start));
+ for (addr = start; addr < end; pgd++, addr = next) {
+ next = pgd_addr_end(addr, end);
+ unmap_pud_range_nofree(pgd, addr, next);
+ }
+}
+
+/*
+ * Page table pages are page-aligned. The lower half of the top
+ * level is used for userspace and the top half for the kernel.
+ * This returns true for user pages that need to get copied into
+ * both the user and kernel copies of the page tables, and false
+ * for kernel pages that should only be in the kernel copy.
+ */
+static inline bool is_userspace_pgd(pgd_t *pgdp)
+{
+ return ((unsigned long)pgdp % PAGE_SIZE) < (PAGE_SIZE / 2);
+}
+
+pgd_t kaiser_set_shadow_pgd(pgd_t *pgdp, pgd_t pgd)
+{
+ if (!kaiser_enabled)
+ return pgd;
+ /*
+ * Do we need to also populate the shadow pgd? Check _PAGE_USER to
+ * skip cases like kexec and EFI which make temporary low mappings.
+ */
+ if (pgd.pgd & _PAGE_USER) {
+ if (is_userspace_pgd(pgdp)) {
+ native_get_shadow_pgd(pgdp)->pgd = pgd.pgd;
+ /*
+ * Even if the entry is *mapping* userspace, ensure
+ * that userspace can not use it. This way, if we
+ * get out to userspace running on the kernel CR3,
+ * userspace will crash instead of running.
+ */
+ if (__supported_pte_mask & _PAGE_NX)
+ pgd.pgd |= _PAGE_NX;
+ }
+ } else if (!pgd.pgd) {
+ /*
+ * pgd_clear() cannot check _PAGE_USER, and is even used to
+ * clear corrupted pgd entries: so just rely on cases like
+ * kexec and EFI never to be using pgd_clear().
+ */
+ if (!WARN_ON_ONCE((unsigned long)pgdp & PAGE_SIZE) &&
+ is_userspace_pgd(pgdp))
+ native_get_shadow_pgd(pgdp)->pgd = pgd.pgd;
+ }
+ return pgd;
+}
+
+void kaiser_setup_pcid(void)
+{
+ unsigned long user_cr3 = KAISER_SHADOW_PGD_OFFSET;
+
+ if (this_cpu_has(X86_FEATURE_PCID))
+ user_cr3 |= X86_CR3_PCID_USER_NOFLUSH;
+ /*
+ * These variables are used by the entry/exit
+ * code to change PCID and pgd and TLB flushing.
+ */
+ this_cpu_write(x86_cr3_pcid_user, user_cr3);
+}
+
+/*
+ * Make a note that this cpu will need to flush USER tlb on return to user.
+ * If cpu does not have PCID, then the NOFLUSH bit will never have been set.
+ */
+void kaiser_flush_tlb_on_return_to_user(void)
+{
+ if (this_cpu_has(X86_FEATURE_PCID))
+ this_cpu_write(x86_cr3_pcid_user,
+ X86_CR3_PCID_USER_FLUSH | KAISER_SHADOW_PGD_OFFSET);
+}
+EXPORT_SYMBOL(kaiser_flush_tlb_on_return_to_user);
diff --git a/arch/x86/mm/kaslr.c b/arch/x86/mm/kaslr.c
index aed2064..319183d 100644
--- a/arch/x86/mm/kaslr.c
+++ b/arch/x86/mm/kaslr.c
@@ -189,6 +189,6 @@
*pud_tramp = *pud;
}
- set_pgd(&trampoline_pgd_entry,
- __pgd(_KERNPG_TABLE | __pa(pud_page_tramp)));
+ /* Avoid set_pgd(), in case it's complicated by CONFIG_PAGE_TABLE_ISOLATION */
+ trampoline_pgd_entry = __pgd(_KERNPG_TABLE | __pa(pud_page_tramp));
}
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c
index e3353c9..73dcb0e1 100644
--- a/arch/x86/mm/pageattr.c
+++ b/arch/x86/mm/pageattr.c
@@ -52,6 +52,7 @@
#define CPA_FLUSHTLB 1
#define CPA_ARRAY 2
#define CPA_PAGES_ARRAY 4
+#define CPA_FREE_PAGETABLES 8
#ifdef CONFIG_PROC_FS
static unsigned long direct_pages_count[PG_LEVEL_NUM];
@@ -729,10 +730,13 @@
return 0;
}
-static bool try_to_free_pte_page(pte_t *pte)
+static bool try_to_free_pte_page(struct cpa_data *cpa, pte_t *pte)
{
int i;
+ if (!(cpa->flags & CPA_FREE_PAGETABLES))
+ return false;
+
for (i = 0; i < PTRS_PER_PTE; i++)
if (!pte_none(pte[i]))
return false;
@@ -741,10 +745,13 @@
return true;
}
-static bool try_to_free_pmd_page(pmd_t *pmd)
+static bool try_to_free_pmd_page(struct cpa_data *cpa, pmd_t *pmd)
{
int i;
+ if (!(cpa->flags & CPA_FREE_PAGETABLES))
+ return false;
+
for (i = 0; i < PTRS_PER_PMD; i++)
if (!pmd_none(pmd[i]))
return false;
@@ -753,7 +760,9 @@
return true;
}
-static bool unmap_pte_range(pmd_t *pmd, unsigned long start, unsigned long end)
+static bool unmap_pte_range(struct cpa_data *cpa, pmd_t *pmd,
+ unsigned long start,
+ unsigned long end)
{
pte_t *pte = pte_offset_kernel(pmd, start);
@@ -764,22 +773,23 @@
pte++;
}
- if (try_to_free_pte_page((pte_t *)pmd_page_vaddr(*pmd))) {
+ if (try_to_free_pte_page(cpa, (pte_t *)pmd_page_vaddr(*pmd))) {
pmd_clear(pmd);
return true;
}
return false;
}
-static void __unmap_pmd_range(pud_t *pud, pmd_t *pmd,
+static void __unmap_pmd_range(struct cpa_data *cpa, pud_t *pud, pmd_t *pmd,
unsigned long start, unsigned long end)
{
- if (unmap_pte_range(pmd, start, end))
- if (try_to_free_pmd_page((pmd_t *)pud_page_vaddr(*pud)))
+ if (unmap_pte_range(cpa, pmd, start, end))
+ if (try_to_free_pmd_page(cpa, (pmd_t *)pud_page_vaddr(*pud)))
pud_clear(pud);
}
-static void unmap_pmd_range(pud_t *pud, unsigned long start, unsigned long end)
+static void unmap_pmd_range(struct cpa_data *cpa, pud_t *pud,
+ unsigned long start, unsigned long end)
{
pmd_t *pmd = pmd_offset(pud, start);
@@ -790,7 +800,7 @@
unsigned long next_page = (start + PMD_SIZE) & PMD_MASK;
unsigned long pre_end = min_t(unsigned long, end, next_page);
- __unmap_pmd_range(pud, pmd, start, pre_end);
+ __unmap_pmd_range(cpa, pud, pmd, start, pre_end);
start = pre_end;
pmd++;
@@ -803,7 +813,8 @@
if (pmd_large(*pmd))
pmd_clear(pmd);
else
- __unmap_pmd_range(pud, pmd, start, start + PMD_SIZE);
+ __unmap_pmd_range(cpa, pud, pmd,
+ start, start + PMD_SIZE);
start += PMD_SIZE;
pmd++;
@@ -813,17 +824,19 @@
* 4K leftovers?
*/
if (start < end)
- return __unmap_pmd_range(pud, pmd, start, end);
+ return __unmap_pmd_range(cpa, pud, pmd, start, end);
/*
* Try again to free the PMD page if haven't succeeded above.
*/
if (!pud_none(*pud))
- if (try_to_free_pmd_page((pmd_t *)pud_page_vaddr(*pud)))
+ if (try_to_free_pmd_page(cpa, (pmd_t *)pud_page_vaddr(*pud)))
pud_clear(pud);
}
-static void unmap_pud_range(pgd_t *pgd, unsigned long start, unsigned long end)
+static void __unmap_pud_range(struct cpa_data *cpa, pgd_t *pgd,
+ unsigned long start,
+ unsigned long end)
{
pud_t *pud = pud_offset(pgd, start);
@@ -834,7 +847,7 @@
unsigned long next_page = (start + PUD_SIZE) & PUD_MASK;
unsigned long pre_end = min_t(unsigned long, end, next_page);
- unmap_pmd_range(pud, start, pre_end);
+ unmap_pmd_range(cpa, pud, start, pre_end);
start = pre_end;
pud++;
@@ -848,7 +861,7 @@
if (pud_large(*pud))
pud_clear(pud);
else
- unmap_pmd_range(pud, start, start + PUD_SIZE);
+ unmap_pmd_range(cpa, pud, start, start + PUD_SIZE);
start += PUD_SIZE;
pud++;
@@ -858,7 +871,7 @@
* 2M leftovers?
*/
if (start < end)
- unmap_pmd_range(pud, start, end);
+ unmap_pmd_range(cpa, pud, start, end);
/*
* No need to try to free the PUD page because we'll free it in
@@ -866,6 +879,24 @@
*/
}
+static void unmap_pud_range(pgd_t *pgd, unsigned long start, unsigned long end)
+{
+ struct cpa_data cpa = {
+ .flags = CPA_FREE_PAGETABLES,
+ };
+
+ __unmap_pud_range(&cpa, pgd, start, end);
+}
+
+void unmap_pud_range_nofree(pgd_t *pgd, unsigned long start, unsigned long end)
+{
+ struct cpa_data cpa = {
+ .flags = 0,
+ };
+
+ __unmap_pud_range(&cpa, pgd, start, end);
+}
+
static int alloc_pte_page(pmd_t *pmd)
{
pte_t *pte = (pte_t *)get_zeroed_page(GFP_KERNEL | __GFP_NOTRACK);
diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c
index 3feec5a..209b946 100644
--- a/arch/x86/mm/pgtable.c
+++ b/arch/x86/mm/pgtable.c
@@ -344,14 +344,15 @@
kmem_cache_free(pgd_cache, pgd);
}
#else
+
static inline pgd_t *_pgd_alloc(void)
{
- return (pgd_t *)__get_free_page(PGALLOC_GFP);
+ return (pgd_t *)__get_free_pages(PGALLOC_GFP, PGD_ALLOCATION_ORDER);
}
static inline void _pgd_free(pgd_t *pgd)
{
- free_page((unsigned long)pgd);
+ free_pages((unsigned long)pgd, PGD_ALLOCATION_ORDER);
}
#endif /* CONFIG_X86_PAE */
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index 75fb011..578973a 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -6,16 +6,17 @@
#include <linux/interrupt.h>
#include <linux/export.h>
#include <linux/cpu.h>
+#include <linux/debugfs.h>
#include <asm/tlbflush.h>
#include <asm/mmu_context.h>
#include <asm/cache.h>
#include <asm/apic.h>
#include <asm/uv/uv.h>
-#include <linux/debugfs.h>
+#include <asm/kaiser.h>
/*
- * Smarter SMP flushing macros.
+ * TLB flushing, formerly SMP-only
* c/o Linus Torvalds.
*
* These mean you can really definitely utterly forget about
@@ -28,14 +29,42 @@
* Implement flush IPI by CALL_FUNCTION_VECTOR, Alex Shi
*/
-#ifdef CONFIG_SMP
-
struct flush_tlb_info {
struct mm_struct *flush_mm;
unsigned long flush_start;
unsigned long flush_end;
};
+static void load_new_mm_cr3(pgd_t *pgdir)
+{
+ unsigned long new_mm_cr3 = __pa(pgdir);
+
+ if (kaiser_enabled) {
+ /*
+ * We reuse the same PCID for different tasks, so we must
+ * flush all the entries for the PCID out when we change tasks.
+ * Flush KERN below, flush USER when returning to userspace in
+ * kaiser's SWITCH_USER_CR3 (_SWITCH_TO_USER_CR3) macro.
+ *
+ * invpcid_flush_single_context(X86_CR3_PCID_ASID_USER) could
+ * do it here, but can only be used if X86_FEATURE_INVPCID is
+ * available - and many machines support pcid without invpcid.
+ *
+ * If X86_CR3_PCID_KERN_FLUSH actually added something, then it
+ * would be needed in the write_cr3() below - if PCIDs enabled.
+ */
+ BUILD_BUG_ON(X86_CR3_PCID_KERN_FLUSH);
+ kaiser_flush_tlb_on_return_to_user();
+ }
+
+ /*
+ * Caution: many callers of this function expect
+ * that load_cr3() is serializing and orders TLB
+ * fills with respect to the mm_cpumask writes.
+ */
+ write_cr3(new_mm_cr3);
+}
+
/*
* We cannot call mmdrop() because we are in interrupt context,
* instead update mm->cpu_vm_mask.
@@ -47,7 +76,7 @@
BUG();
if (cpumask_test_cpu(cpu, mm_cpumask(active_mm))) {
cpumask_clear_cpu(cpu, mm_cpumask(active_mm));
- load_cr3(swapper_pg_dir);
+ load_new_mm_cr3(swapper_pg_dir);
/*
* This gets called in the idle path where RCU
* functions differently. Tracing normally
@@ -59,8 +88,6 @@
}
EXPORT_SYMBOL_GPL(leave_mm);
-#endif /* CONFIG_SMP */
-
void switch_mm(struct mm_struct *prev, struct mm_struct *next,
struct task_struct *tsk)
{
@@ -83,7 +110,7 @@
* mapped in the new pgd, we'll double-fault. Forcibly
* map it.
*/
- unsigned int stack_pgd_index = pgd_index(current_stack_pointer());
+ unsigned int stack_pgd_index = pgd_index(current_stack_pointer);
pgd_t *pgd = next->pgd + stack_pgd_index;
@@ -91,10 +118,8 @@
set_pgd(pgd, init_mm.pgd[stack_pgd_index]);
}
-#ifdef CONFIG_SMP
this_cpu_write(cpu_tlbstate.state, TLBSTATE_OK);
this_cpu_write(cpu_tlbstate.active_mm, next);
-#endif
cpumask_set_cpu(cpu, mm_cpumask(next));
@@ -126,7 +151,7 @@
* ordering guarantee we need.
*
*/
- load_cr3(next->pgd);
+ load_new_mm_cr3(next->pgd);
trace_tlb_flush(TLB_FLUSH_ON_TASK_SWITCH, TLB_FLUSH_ALL);
@@ -152,9 +177,7 @@
if (unlikely(prev->context.ldt != next->context.ldt))
load_mm_ldt(next);
#endif
- }
-#ifdef CONFIG_SMP
- else {
+ } else {
this_cpu_write(cpu_tlbstate.state, TLBSTATE_OK);
BUG_ON(this_cpu_read(cpu_tlbstate.active_mm) != next);
@@ -175,17 +198,14 @@
* As above, load_cr3() is serializing and orders TLB
* fills with respect to the mm_cpumask write.
*/
- load_cr3(next->pgd);
+ load_new_mm_cr3(next->pgd);
trace_tlb_flush(TLB_FLUSH_ON_TASK_SWITCH, TLB_FLUSH_ALL);
load_mm_cr4(next);
load_mm_ldt(next);
}
}
-#endif
}
-#ifdef CONFIG_SMP
-
/*
* The flush IPI assumes that a thread switch happens in this order:
* [cpu0: the cpu that switches]
@@ -287,23 +307,6 @@
smp_call_function_many(cpumask, flush_tlb_func, &info, 1);
}
-void flush_tlb_current_task(void)
-{
- struct mm_struct *mm = current->mm;
-
- preempt_disable();
-
- count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ALL);
-
- /* This is an implicit full barrier that synchronizes with switch_mm. */
- local_flush_tlb();
-
- trace_tlb_flush(TLB_LOCAL_SHOOTDOWN, TLB_FLUSH_ALL);
- if (cpumask_any_but(mm_cpumask(mm), smp_processor_id()) < nr_cpu_ids)
- flush_tlb_others(mm_cpumask(mm), mm, 0UL, TLB_FLUSH_ALL);
- preempt_enable();
-}
-
/*
* See Documentation/x86/tlb.txt for details. We choose 33
* because it is large enough to cover the vast majority (at
@@ -324,6 +327,12 @@
unsigned long base_pages_to_flush = TLB_FLUSH_ALL;
preempt_disable();
+
+ if ((end != TLB_FLUSH_ALL) && !(vmflag & VM_HUGETLB))
+ base_pages_to_flush = (end - start) >> PAGE_SHIFT;
+ if (base_pages_to_flush > tlb_single_page_flush_ceiling)
+ base_pages_to_flush = TLB_FLUSH_ALL;
+
if (current->active_mm != mm) {
/* Synchronize with switch_mm. */
smp_mb();
@@ -340,15 +349,11 @@
goto out;
}
- if ((end != TLB_FLUSH_ALL) && !(vmflag & VM_HUGETLB))
- base_pages_to_flush = (end - start) >> PAGE_SHIFT;
-
/*
* Both branches below are implicit full barriers (MOV to CR or
* INVLPG) that synchronize with switch_mm.
*/
- if (base_pages_to_flush > tlb_single_page_flush_ceiling) {
- base_pages_to_flush = TLB_FLUSH_ALL;
+ if (base_pages_to_flush == TLB_FLUSH_ALL) {
count_vm_tlb_event(NR_TLB_LOCAL_FLUSH_ALL);
local_flush_tlb();
} else {
@@ -369,33 +374,6 @@
preempt_enable();
}
-void flush_tlb_page(struct vm_area_struct *vma, unsigned long start)
-{
- struct mm_struct *mm = vma->vm_mm;
-
- preempt_disable();
-
- if (current->active_mm == mm) {
- if (current->mm) {
- /*
- * Implicit full barrier (INVLPG) that synchronizes
- * with switch_mm.
- */
- __flush_tlb_one(start);
- } else {
- leave_mm(smp_processor_id());
-
- /* Synchronize with switch_mm. */
- smp_mb();
- }
- }
-
- if (cpumask_any_but(mm_cpumask(mm), smp_processor_id()) < nr_cpu_ids)
- flush_tlb_others(mm_cpumask(mm), mm, start, start + PAGE_SIZE);
-
- preempt_enable();
-}
-
static void do_flush_tlb_all(void *info)
{
count_vm_tlb_event(NR_TLB_REMOTE_FLUSH_RECEIVED);
@@ -480,5 +458,3 @@
return 0;
}
late_initcall(create_tlb_single_page_flush_ceiling);
-
-#endif /* CONFIG_SMP */
diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c
index 2f25a36..dcb2d9d 100644
--- a/arch/x86/platform/efi/efi_64.c
+++ b/arch/x86/platform/efi/efi_64.c
@@ -142,7 +142,7 @@
return 0;
gfp_mask = GFP_KERNEL | __GFP_NOTRACK | __GFP_ZERO;
- efi_pgd = (pgd_t *)__get_free_page(gfp_mask);
+ efi_pgd = (pgd_t *)__get_free_pages(gfp_mask, PGD_ALLOCATION_ORDER);
if (!efi_pgd)
return -ENOMEM;
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index 8f1f7ef..2bea87c 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -444,6 +444,12 @@
~((1 << X86_FEATURE_MTRR) | /* disable MTRR */
(1 << X86_FEATURE_ACC)); /* thermal monitoring */
+ /*
+ * Xen PV would need some work to support PCID: CR3 handling as well
+ * as xen_flush_tlb_others() would need updating.
+ */
+ cpuid_leaf1_ecx_mask &= ~(1 << (X86_FEATURE_PCID % 32)); /* disable PCID */
+
if (!xen_initial_domain())
cpuid_leaf1_edx_mask &=
~((1 << X86_FEATURE_ACPI)); /* disable ACPI */
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index b08ccbb..8ba0af7 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -185,7 +185,8 @@
}
wb_congested = wb_congested_get_create(&q->backing_dev_info,
- blkcg->css.id, GFP_NOWAIT);
+ blkcg->css.id,
+ GFP_NOWAIT | __GFP_NOWARN);
if (!wb_congested) {
ret = -ENOMEM;
goto err_put_css;
@@ -193,7 +194,7 @@
/* allocate */
if (!new_blkg) {
- new_blkg = blkg_alloc(blkcg, q, GFP_NOWAIT);
+ new_blkg = blkg_alloc(blkcg, q, GFP_NOWAIT | __GFP_NOWARN);
if (unlikely(!new_blkg)) {
ret = -ENOMEM;
goto err_put_congested;
@@ -1022,7 +1023,7 @@
}
spin_lock_init(&blkcg->lock);
- INIT_RADIX_TREE(&blkcg->blkg_tree, GFP_NOWAIT);
+ INIT_RADIX_TREE(&blkcg->blkg_tree, GFP_NOWAIT | __GFP_NOWARN);
INIT_HLIST_HEAD(&blkcg->blkg_list);
#ifdef CONFIG_CGROUP_WRITEBACK
INIT_LIST_HEAD(&blkcg->cgwb_list);
@@ -1240,7 +1241,7 @@
if (blkg->pd[pol->plid])
continue;
- pd = pol->pd_alloc_fn(GFP_NOWAIT, q->node);
+ pd = pol->pd_alloc_fn(GFP_NOWAIT | __GFP_NOWARN, q->node);
if (!pd)
swap(pd, pd_prealloc);
if (!pd) {
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index 4ac4910..6a90155 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -3868,7 +3868,8 @@
goto out;
}
- cfqq = kmem_cache_alloc_node(cfq_pool, GFP_NOWAIT | __GFP_ZERO,
+ cfqq = kmem_cache_alloc_node(cfq_pool,
+ GFP_NOWAIT | __GFP_ZERO | __GFP_NOWARN,
cfqd->queue->node);
if (!cfqq) {
cfqq = &cfqd->oom_cfqq;
diff --git a/crypto/algapi.c b/crypto/algapi.c
index 1fad2a6..5c098ff 100644
--- a/crypto/algapi.c
+++ b/crypto/algapi.c
@@ -167,6 +167,18 @@
spawn->alg = NULL;
spawns = &inst->alg.cra_users;
+
+ /*
+ * We may encounter an unregistered instance here, since
+ * an instance's spawns are set up prior to the instance
+ * being registered. An unregistered instance will have
+ * NULL ->cra_users.next, since ->cra_users isn't
+ * properly initialized until registration. But an
+ * unregistered instance cannot have any users, so treat
+ * it the same as ->cra_users being empty.
+ */
+ if (spawns->next == NULL)
+ break;
}
} while ((spawns = crypto_more_spawns(alg, &stack, &top,
&secondary_spawns)));
diff --git a/crypto/chacha20poly1305.c b/crypto/chacha20poly1305.c
index e899ef5..cb1c3a3 100644
--- a/crypto/chacha20poly1305.c
+++ b/crypto/chacha20poly1305.c
@@ -610,6 +610,11 @@
algt->mask));
if (IS_ERR(poly))
return PTR_ERR(poly);
+ poly_hash = __crypto_hash_alg_common(poly);
+
+ err = -EINVAL;
+ if (poly_hash->digestsize != POLY1305_DIGEST_SIZE)
+ goto out_put_poly;
err = -ENOMEM;
inst = kzalloc(sizeof(*inst) + sizeof(*ctx), GFP_KERNEL);
@@ -618,7 +623,6 @@
ctx = aead_instance_ctx(inst);
ctx->saltlen = CHACHAPOLY_IV_SIZE - ivsize;
- poly_hash = __crypto_hash_alg_common(poly);
err = crypto_init_ahash_spawn(&ctx->poly, poly_hash,
aead_crypto_instance(inst));
if (err)
diff --git a/crypto/pcrypt.c b/crypto/pcrypt.c
index ee9cfb9..f8ec3d4 100644
--- a/crypto/pcrypt.c
+++ b/crypto/pcrypt.c
@@ -254,6 +254,14 @@
crypto_free_aead(ctx->child);
}
+static void pcrypt_free(struct aead_instance *inst)
+{
+ struct pcrypt_instance_ctx *ctx = aead_instance_ctx(inst);
+
+ crypto_drop_aead(&ctx->spawn);
+ kfree(inst);
+}
+
static int pcrypt_init_instance(struct crypto_instance *inst,
struct crypto_alg *alg)
{
@@ -319,6 +327,8 @@
inst->alg.encrypt = pcrypt_aead_encrypt;
inst->alg.decrypt = pcrypt_aead_decrypt;
+ inst->free = pcrypt_free;
+
err = aead_register_instance(tmpl, inst);
if (err)
goto out_drop_aead;
@@ -349,14 +359,6 @@
return -EINVAL;
}
-static void pcrypt_free(struct crypto_instance *inst)
-{
- struct pcrypt_instance_ctx *ctx = crypto_instance_ctx(inst);
-
- crypto_drop_aead(&ctx->spawn);
- kfree(inst);
-}
-
static int pcrypt_cpumask_change_notify(struct notifier_block *self,
unsigned long val, void *data)
{
@@ -469,7 +471,6 @@
static struct crypto_template pcrypt_tmpl = {
.name = "pcrypt",
.create = pcrypt_create,
- .free = pcrypt_free,
.module = THIS_MODULE,
};
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index d02e7c0..0651010 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -235,6 +235,9 @@
config GENERIC_CPU_AUTOPROBE
bool
+config GENERIC_CPU_VULNERABILITIES
+ bool
+
config SOC_BUS
bool
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index e17ad53..4fe86f7 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -590,10 +590,58 @@
#endif
}
+#ifdef CONFIG_GENERIC_CPU_VULNERABILITIES
+
+ssize_t __weak cpu_show_meltdown(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "Not affected\n");
+}
+
+ssize_t __weak cpu_show_spectre_v1(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "Not affected\n");
+}
+
+ssize_t __weak cpu_show_spectre_v2(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "Not affected\n");
+}
+
+static DEVICE_ATTR(meltdown, 0444, cpu_show_meltdown, NULL);
+static DEVICE_ATTR(spectre_v1, 0444, cpu_show_spectre_v1, NULL);
+static DEVICE_ATTR(spectre_v2, 0444, cpu_show_spectre_v2, NULL);
+
+static struct attribute *cpu_root_vulnerabilities_attrs[] = {
+ &dev_attr_meltdown.attr,
+ &dev_attr_spectre_v1.attr,
+ &dev_attr_spectre_v2.attr,
+ NULL
+};
+
+static const struct attribute_group cpu_root_vulnerabilities_group = {
+ .name = "vulnerabilities",
+ .attrs = cpu_root_vulnerabilities_attrs,
+};
+
+static void __init cpu_register_vulnerabilities(void)
+{
+ if (sysfs_create_group(&cpu_subsys.dev_root->kobj,
+ &cpu_root_vulnerabilities_group))
+ pr_err("Unable to register CPU vulnerabilities\n");
+}
+
+#else
+static inline void cpu_register_vulnerabilities(void) { }
+#endif
+
void __init cpu_dev_init(void)
{
if (subsys_system_register(&cpu_subsys, cpu_root_attr_groups))
panic("Failed to register CPU subsystem");
cpu_dev_register_generic();
+ cpu_register_vulnerabilities();
}
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 7d506cb..4d30da2 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -272,6 +272,7 @@
int result, flags;
struct nbd_request request;
unsigned long size = blk_rq_bytes(req);
+ struct bio *bio;
u32 type;
if (req->cmd_type == REQ_TYPE_DRV_PRIV)
@@ -305,16 +306,20 @@
return -EIO;
}
- if (type == NBD_CMD_WRITE) {
- struct req_iterator iter;
+ if (type != NBD_CMD_WRITE)
+ return 0;
+
+ flags = 0;
+ bio = req->bio;
+ while (bio) {
+ struct bio *next = bio->bi_next;
+ struct bvec_iter iter;
struct bio_vec bvec;
- /*
- * we are really probing at internals to determine
- * whether to set MSG_MORE or not...
- */
- rq_for_each_segment(bvec, req, iter) {
- flags = 0;
- if (!rq_iter_last(bvec, iter))
+
+ bio_for_each_segment(bvec, bio, iter) {
+ bool is_last = !next && bio_iter_last(bvec, iter);
+
+ if (is_last)
flags = MSG_MORE;
dev_dbg(nbd_to_dev(nbd), "request %p: sending %d bytes data\n",
cmd, bvec.bv_len);
@@ -325,7 +330,16 @@
result);
return -EIO;
}
+ /*
+ * The completion might already have come in,
+ * so break for the last one instead of letting
+ * the iterator do it. This prevents use-after-free
+ * of the bio.
+ */
+ if (is_last)
+ break;
}
+ bio = next;
}
return 0;
}
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 24f4b54..e32badd 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -4511,7 +4511,7 @@
segment_size = rbd_obj_bytes(&rbd_dev->header);
blk_queue_max_hw_sectors(q, segment_size / SECTOR_SIZE);
q->limits.max_sectors = queue_max_hw_sectors(q);
- blk_queue_max_segments(q, segment_size / SECTOR_SIZE);
+ blk_queue_max_segments(q, USHRT_MAX);
blk_queue_max_segment_size(q, segment_size);
blk_queue_io_min(q, segment_size);
blk_queue_io_opt(q, segment_size);
diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c
index 795c9d9..2051d92 100644
--- a/drivers/bus/sunxi-rsb.c
+++ b/drivers/bus/sunxi-rsb.c
@@ -178,6 +178,7 @@
.match = sunxi_rsb_device_match,
.probe = sunxi_rsb_device_probe,
.remove = sunxi_rsb_device_remove,
+ .uevent = of_device_uevent_modalias,
};
static void sunxi_rsb_dev_release(struct device *dev)
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 177fb3d..3f35d54 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -28,6 +28,8 @@
#include <soc/qcom/glink.h>
#include <soc/qcom/subsystem_notif.h>
#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/service-notifier.h>
+#include <soc/qcom/service-locator.h>
#include <linux/scatterlist.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
@@ -58,6 +60,9 @@
#define VMID_ADSP_Q6 6
#define DEBUGFS_SIZE 1024
+#define AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME "audio_pdr_adsprpc"
+#define AUDIO_PDR_ADSP_SERVICE_NAME "avs/audio"
+
#define RPC_TIMEOUT (5 * HZ)
#define BALIGN 128
#define NUM_CHANNELS 4 /* adsp, mdsp, slpi, cdsp*/
@@ -110,6 +115,9 @@
static int fastrpc_glink_open(int cid);
static void fastrpc_glink_close(void *chan, int cid);
+static int fastrpc_audio_pdr_notifier_cb(struct notifier_block *nb,
+ unsigned long code,
+ void *data);
static struct dentry *debugfs_root;
static struct dentry *debugfs_global_file;
@@ -224,6 +232,16 @@
int used;
};
+struct fastrpc_static_pd {
+ char *spdname;
+ struct notifier_block pdrnb;
+ struct notifier_block get_service_nb;
+ void *pdrhandle;
+ int pdrcount;
+ int prevpdrcount;
+ int ispdup;
+};
+
struct fastrpc_glink_info {
int link_state;
int port_state;
@@ -238,6 +256,7 @@
void *chan;
struct device *dev;
struct fastrpc_session_ctx session[NUM_SESSIONS];
+ struct fastrpc_static_pd spd[NUM_SESSIONS];
struct completion work;
struct completion workport;
struct notifier_block nb;
@@ -334,6 +353,7 @@
int cid;
int ssrcount;
int pd;
+ char *spdname;
int file_close;
struct fastrpc_apps *apps;
struct hlist_head perf;
@@ -342,6 +362,7 @@
struct pm_qos_request pm_qos_req;
int qos_request;
struct mutex map_mutex;
+ struct mutex fl_map_mutex;
};
static struct fastrpc_apps gfa;
@@ -352,6 +373,14 @@
.subsys = "adsp",
.link.link_info.edge = "lpass",
.link.link_info.transport = "smem",
+ .spd = {
+ {
+ .spdname =
+ AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME,
+ .pdrnb.notifier_call =
+ fastrpc_audio_pdr_notifier_cb,
+ }
+ },
},
{
.name = "mdsprpc-smd",
@@ -491,9 +520,7 @@
} else {
struct fastrpc_file *fl = map->fl;
- spin_lock(&fl->hlock);
hlist_add_head(&map->hn, &fl->maps);
- spin_unlock(&fl->hlock);
}
}
@@ -522,7 +549,6 @@
}
spin_unlock(&me->hlock);
} else {
- spin_lock(&fl->hlock);
hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
if (va >= map->va &&
va + len <= map->va + map->len &&
@@ -533,7 +559,6 @@
break;
}
}
- spin_unlock(&fl->hlock);
}
if (match) {
*ppmap = match;
@@ -581,7 +606,6 @@
*ppmap = match;
return 0;
}
- spin_lock(&fl->hlock);
hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
if (map->raddr == va &&
map->raddr + map->len == va + len &&
@@ -591,7 +615,6 @@
break;
}
}
- spin_unlock(&fl->hlock);
if (match) {
*ppmap = match;
return 0;
@@ -619,11 +642,9 @@
if (map->refs > 0)
return;
} else {
- spin_lock(&fl->hlock);
map->refs--;
if (!map->refs)
hlist_del_init(&map->hn);
- spin_unlock(&fl->hlock);
if (map->refs > 0 && !flags)
return;
}
@@ -1084,8 +1105,11 @@
spin_lock(&ctx->fl->hlock);
hlist_del_init(&ctx->hn);
spin_unlock(&ctx->fl->hlock);
+ mutex_lock(&ctx->fl->fl_map_mutex);
for (i = 0; i < nbufs; ++i)
fastrpc_mmap_free(ctx->maps[i], 0);
+
+ mutex_unlock(&ctx->fl->fl_map_mutex);
fastrpc_buf_free(ctx->buf, 1);
ctx->magic = 0;
kfree(ctx);
@@ -1127,6 +1151,21 @@
spin_unlock(&me->hlock);
}
+
+static void fastrpc_notify_pdr_drivers(struct fastrpc_apps *me, char *spdname)
+{
+ struct fastrpc_file *fl;
+ struct hlist_node *n;
+
+ spin_lock(&me->hlock);
+ hlist_for_each_entry_safe(fl, n, &me->drivers, hn) {
+ if (fl->spdname && !strcmp(spdname, fl->spdname))
+ fastrpc_notify_users(fl);
+ }
+ spin_unlock(&me->hlock);
+
+}
+
static void context_list_ctor(struct fastrpc_ctx_lst *me)
{
INIT_HLIST_HEAD(&me->interrupted);
@@ -1215,21 +1254,27 @@
uintptr_t buf = (uintptr_t)lpra[i].buf.pv;
size_t len = lpra[i].buf.len;
+ mutex_lock(&ctx->fl->fl_map_mutex);
if (ctx->fds[i] && (ctx->fds[i] != -1))
fastrpc_mmap_create(ctx->fl, ctx->fds[i],
ctx->attrs[i], buf, len,
mflags, &ctx->maps[i]);
+ mutex_unlock(&ctx->fl->fl_map_mutex);
ipage += 1;
}
PERF_END);
handles = REMOTE_SCALARS_INHANDLES(sc) + REMOTE_SCALARS_OUTHANDLES(sc);
+ mutex_lock(&ctx->fl->fl_map_mutex);
for (i = bufs; i < bufs + handles; i++) {
VERIFY(err, !fastrpc_mmap_create(ctx->fl, ctx->fds[i],
FASTRPC_ATTR_NOVA, 0, 0, 0, &ctx->maps[i]));
- if (err)
+ if (err) {
+ mutex_unlock(&ctx->fl->fl_map_mutex);
goto bail;
+ }
ipage += 1;
}
+ mutex_unlock(&ctx->fl->fl_map_mutex);
metalen = copylen = (size_t)&ipage[0] + (sizeof(uint64_t) * M_FDLIST) +
(sizeof(uint32_t) * M_CRCLIST);
@@ -1438,10 +1483,13 @@
if (err)
goto bail;
} else {
+ mutex_lock(&ctx->fl->fl_map_mutex);
fastrpc_mmap_free(ctx->maps[i], 0);
+ mutex_unlock(&ctx->fl->fl_map_mutex);
ctx->maps[i] = NULL;
}
}
+ mutex_lock(&ctx->fl->fl_map_mutex);
if (inbufs + outbufs + handles) {
for (i = 0; i < M_FDLIST; i++) {
if (!fdlist[i])
@@ -1451,6 +1499,7 @@
fastrpc_mmap_free(mmap, 0);
}
}
+ mutex_unlock(&ctx->fl->fl_map_mutex);
if (ctx->crc && crclist && rpra)
K_COPY_TO_USER(err, kernel, ctx->crc,
crclist, M_CRCLIST*sizeof(uint32_t));
@@ -1700,7 +1749,28 @@
return err;
}
+static int fastrpc_get_adsp_session(char *name, int *session)
+{
+ struct fastrpc_apps *me = &gfa;
+ int err = 0, i;
+
+ for (i = 0; i < NUM_SESSIONS; i++) {
+ if (!me->channel[0].spd[i].spdname)
+ continue;
+ if (!strcmp(name, me->channel[0].spd[i].spdname))
+ break;
+ }
+ VERIFY(err, i < NUM_SESSIONS);
+ if (err)
+ goto bail;
+ *session = i;
+bail:
+ return err;
+}
+
+static int fastrpc_mmap_remove_pdr(struct fastrpc_file *fl);
static int fastrpc_channel_open(struct fastrpc_file *fl);
+static int fastrpc_mmap_remove_ssr(struct fastrpc_file *fl);
static int fastrpc_init_process(struct fastrpc_file *fl,
struct fastrpc_ioctl_init_attrs *uproc)
{
@@ -1755,8 +1825,10 @@
if (err)
goto bail;
if (init->filelen) {
+ mutex_lock(&fl->fl_map_mutex);
VERIFY(err, !fastrpc_mmap_create(fl, init->filefd, 0,
init->file, init->filelen, mflags, &file));
+ mutex_unlock(&fl->fl_map_mutex);
if (err)
goto bail;
}
@@ -1765,8 +1837,10 @@
init->memlen));
if (err)
goto bail;
+ mutex_lock(&fl->fl_map_mutex);
VERIFY(err, !fastrpc_mmap_create(fl, init->memfd, 0,
init->mem, init->memlen, mflags, &mem));
+ mutex_unlock(&fl->fl_map_mutex);
if (err)
goto bail;
inbuf.pageslen = 1;
@@ -1836,11 +1910,19 @@
inbuf.pgid = current->tgid;
inbuf.namelen = init->filelen;
inbuf.pageslen = 0;
+
+ if (!strcmp(proc_name, "audiopd")) {
+ fl->spdname = AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME;
+ VERIFY(err, !fastrpc_mmap_remove_pdr(fl));
+ }
+
if (!me->staticpd_flags) {
inbuf.pageslen = 1;
+ mutex_lock(&fl->fl_map_mutex);
VERIFY(err, !fastrpc_mmap_create(fl, -1, 0, init->mem,
init->memlen, ADSP_MMAP_REMOTE_HEAP_ADDR,
&mem));
+ mutex_unlock(&fl->fl_map_mutex);
if (err)
goto bail;
phys = mem->phys;
@@ -1897,10 +1979,15 @@
me->channel[fl->cid].rhvm.vmid,
me->channel[fl->cid].rhvm.vmcount,
hlosvm, hlosvmperm, 1);
+ mutex_lock(&fl->fl_map_mutex);
fastrpc_mmap_free(mem, 0);
+ mutex_unlock(&fl->fl_map_mutex);
}
- if (file)
+ if (file) {
+ mutex_lock(&fl->fl_map_mutex);
fastrpc_mmap_free(file, 0);
+ mutex_unlock(&fl->fl_map_mutex);
+ }
return err;
}
@@ -2140,6 +2227,33 @@
return err;
}
+static int fastrpc_mmap_remove_pdr(struct fastrpc_file *fl)
+{
+ struct fastrpc_apps *me = &gfa;
+ int session = 0, err = 0;
+
+ VERIFY(err, !fastrpc_get_adsp_session(
+ AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME, &session));
+ if (err)
+ goto bail;
+ if (me->channel[fl->cid].spd[session].pdrcount !=
+ me->channel[fl->cid].spd[session].prevpdrcount) {
+ if (fastrpc_mmap_remove_ssr(fl))
+ pr_err("ADSPRPC: SSR: Failed to unmap remote heap\n");
+ me->channel[fl->cid].spd[session].prevpdrcount =
+ me->channel[fl->cid].spd[session].pdrcount;
+ }
+ if (!me->channel[fl->cid].spd[session].ispdup) {
+ VERIFY(err, 0);
+ if (err) {
+ err = -ENOTCONN;
+ goto bail;
+ }
+ }
+bail:
+ return err;
+}
+
static int fastrpc_mmap_remove(struct fastrpc_file *fl, uintptr_t va,
size_t len, struct fastrpc_mmap **ppmap);
@@ -2152,16 +2266,23 @@
struct fastrpc_mmap *map = NULL;
mutex_lock(&fl->map_mutex);
+ mutex_lock(&fl->fl_map_mutex);
VERIFY(err, !fastrpc_mmap_remove(fl, ud->vaddrout, ud->size, &map));
+ mutex_unlock(&fl->fl_map_mutex);
if (err)
goto bail;
VERIFY(err, !fastrpc_munmap_on_dsp(fl, map));
if (err)
goto bail;
+ mutex_lock(&fl->fl_map_mutex);
fastrpc_mmap_free(map, 0);
+ mutex_unlock(&fl->fl_map_mutex);
bail:
- if (err && map)
+ if (err && map) {
+ mutex_lock(&fl->fl_map_mutex);
fastrpc_mmap_add(map);
+ mutex_unlock(&fl->fl_map_mutex);
+ }
mutex_unlock(&fl->map_mutex);
return err;
}
@@ -2174,16 +2295,18 @@
VERIFY(err, (fl && ud));
if (err)
goto bail;
-
+ mutex_lock(&fl->fl_map_mutex);
if (!fastrpc_mmap_find(fl, ud->fd, ud->va, ud->len, 0, 0, &map)) {
pr_err("mapping not found to unamp %x va %llx %x\n",
ud->fd, (unsigned long long)ud->va,
(unsigned int)ud->len);
err = -1;
+ mutex_unlock(&fl->fl_map_mutex);
goto bail;
}
if (map)
- fastrpc_mmap_free(map, 0);
+ fastrpc_mmap_free(map, 0);
+ mutex_unlock(&fl->fl_map_mutex);
bail:
return err;
}
@@ -2197,14 +2320,17 @@
int err = 0;
mutex_lock(&fl->map_mutex);
+ mutex_lock(&fl->fl_map_mutex);
if (!fastrpc_mmap_find(fl, ud->fd, (uintptr_t)ud->vaddrin,
- ud->size, ud->flags, 1, &map)){
+ ud->size, ud->flags, 1, &map)) {
+ mutex_unlock(&fl->fl_map_mutex);
mutex_unlock(&fl->map_mutex);
return 0;
}
VERIFY(err, !fastrpc_mmap_create(fl, ud->fd, 0,
(uintptr_t)ud->vaddrin, ud->size,
ud->flags, &map));
+ mutex_unlock(&fl->fl_map_mutex);
if (err)
goto bail;
VERIFY(err, 0 == fastrpc_mmap_on_dsp(fl, ud->flags, map));
@@ -2212,8 +2338,11 @@
goto bail;
ud->vaddrout = map->raddr;
bail:
- if (err && map)
+ if (err && map) {
+ mutex_lock(&fl->fl_map_mutex);
fastrpc_mmap_free(map, 0);
+ mutex_unlock(&fl->fl_map_mutex);
+ }
mutex_unlock(&fl->map_mutex);
return err;
}
@@ -2378,9 +2507,11 @@
spin_unlock(&fl->hlock);
fastrpc_context_list_dtor(fl);
fastrpc_buf_list_free(fl);
+ mutex_lock(&fl->fl_map_mutex);
hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
fastrpc_mmap_free(map, 1);
}
+ mutex_unlock(&fl->fl_map_mutex);
if (fl->ssrcount == fl->apps->channel[cid].ssrcount)
kref_put_mutex(&fl->apps->channel[cid].kref,
fastrpc_channel_close, &fl->apps->smd_mutex);
@@ -2403,6 +2534,7 @@
} while (fperf);
mutex_unlock(&fl->perf_mutex);
mutex_destroy(&fl->perf_mutex);
+ mutex_destroy(&fl->fl_map_mutex);
kfree(fl);
return 0;
}
@@ -2742,6 +2874,7 @@
fl->qos_request = 0;
filp->private_data = fl;
mutex_init(&fl->map_mutex);
+ mutex_init(&fl->fl_map_mutex);
spin_lock(&me->hlock);
hlist_add_head(&fl->hn, &me->drivers);
spin_unlock(&me->hlock);
@@ -3041,6 +3174,64 @@
return NOTIFY_DONE;
}
+static int fastrpc_audio_pdr_notifier_cb(struct notifier_block *pdrnb,
+ unsigned long code,
+ void *data)
+{
+ struct fastrpc_apps *me = &gfa;
+ struct fastrpc_static_pd *spd;
+ struct notif_data *notifdata = data;
+
+ spd = container_of(pdrnb, struct fastrpc_static_pd, pdrnb);
+ if (code == SERVREG_NOTIF_SERVICE_STATE_DOWN_V01) {
+ mutex_lock(&me->smd_mutex);
+ spd->pdrcount++;
+ spd->ispdup = 0;
+ pr_info("ADSPRPC: Audio PDR notifier %d %s\n",
+ MAJOR(me->dev_no), spd->spdname);
+ mutex_unlock(&me->smd_mutex);
+ if (!strcmp(spd->spdname,
+ AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME))
+ me->staticpd_flags = 0;
+ fastrpc_notify_pdr_drivers(me, spd->spdname);
+ } else if (code == SUBSYS_RAMDUMP_NOTIFICATION) {
+ if (me->channel[0].remoteheap_ramdump_dev &&
+ notifdata->enable_ramdump) {
+ me->channel[0].ramdumpenabled = 1;
+ }
+ } else if (code == SERVREG_NOTIF_SERVICE_STATE_UP_V01) {
+ spd->ispdup = 1;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int fastrpc_get_service_location_notify(struct notifier_block *nb,
+ unsigned long opcode, void *data)
+{
+ struct fastrpc_static_pd *spd;
+ struct pd_qmi_client_data *pdr = data;
+ int curr_state = 0;
+
+ spd = container_of(nb, struct fastrpc_static_pd, get_service_nb);
+ if (opcode == LOCATOR_DOWN) {
+ pr_err("ADSPRPC: Audio PD restart notifier locator down\n");
+ return NOTIFY_DONE;
+ }
+
+ if (pdr->total_domains == 1) {
+ spd->pdrhandle = service_notif_register_notifier(
+ pdr->domain_list[0].name,
+ pdr->domain_list[0].instance_id,
+ &spd->pdrnb, &curr_state);
+ if (IS_ERR(spd->pdrhandle))
+ pr_err("ADSPRPC: Unable to register notifier\n");
+ } else
+ pr_err("ADSPRPC: Service returned invalid domains\n");
+
+ return NOTIFY_DONE;
+}
+
static const struct file_operations fops = {
.open = fastrpc_device_open,
.release = fastrpc_device_release,
@@ -3170,6 +3361,7 @@
struct platform_device *ion_pdev;
struct cma *cma;
uint32_t val;
+ int ret = 0;
if (of_device_is_compatible(dev->of_node,
@@ -3224,7 +3416,26 @@
}
return 0;
}
+ if (of_property_read_bool(dev->of_node,
+ "qcom,fastrpc-adsp-audio-pdr")) {
+ int session;
+ VERIFY(err, !fastrpc_get_adsp_session(
+ AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME, &session));
+ if (err)
+ goto spdbail;
+ me->channel[0].spd[session].get_service_nb.notifier_call =
+ fastrpc_get_service_location_notify;
+ ret = get_service_location(
+ AUDIO_PDR_SERVICE_LOCATION_CLIENT_NAME,
+ AUDIO_PDR_ADSP_SERVICE_NAME,
+ &me->channel[0].spd[session].get_service_nb);
+ if (ret)
+ pr_err("ADSPRPC: Get service location failed: %d\n",
+ ret);
+ }
+spdbail:
+ err = 0;
VERIFY(err, !of_platform_populate(pdev->dev.of_node,
fastrpc_match_table,
NULL, &pdev->dev));
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 83f44ce..1155087 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2018, 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
@@ -594,14 +594,15 @@
* Check if command is valid. If the command is asking for
* status, then the processor mask field is to be ignored.
*/
- if ((version != 2) || (cmd > STATUS_STM) ||
- ((cmd != STATUS_STM) && ((mask == 0) || (0 != (mask >> 4))))) {
+ if ((version != 2) || (cmd > STM_AUTO_QUERY) ||
+ ((cmd != STATUS_STM && cmd != STM_AUTO_QUERY) &&
+ ((mask == 0) || (0 != (mask >> 4))))) {
/* Command is invalid. Send bad param message response */
dest_buf[0] = BAD_PARAM_RESPONSE_MESSAGE;
for (i = 0; i < STM_CMD_NUM_BYTES; i++)
dest_buf[i+1] = *(buf + i);
return STM_CMD_NUM_BYTES+1;
- } else if (cmd != STATUS_STM) {
+ } else if (cmd != STATUS_STM && cmd != STM_AUTO_QUERY) {
if (mask & DIAG_STM_MODEM)
diag_process_stm_mask(cmd, DIAG_STM_MODEM,
PERIPHERAL_MODEM);
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
index 848ad87..4a0ee11 100644
--- a/drivers/char/diag/diagfwd_cntl.h
+++ b/drivers/char/diag/diagfwd_cntl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2018, 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
@@ -77,7 +77,7 @@
#define DISABLE_STM 0
#define ENABLE_STM 1
#define STATUS_STM 2
-
+#define STM_AUTO_QUERY 3
#define UPDATE_PERIPHERAL_STM_STATE 1
#define CLEAR_PERIPHERAL_STM_STATE 2
diff --git a/drivers/clk/msm/clock-gcc-8953.c b/drivers/clk/msm/clock-gcc-8953.c
index e25da83..b2dc3d26 100644
--- a/drivers/clk/msm/clock-gcc-8953.c
+++ b/drivers/clk/msm/clock-gcc-8953.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, 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
@@ -4072,8 +4072,17 @@
struct resource *res;
int ret;
u32 regval;
+ struct clk *xo_clk;
bool compat_bin = false;
+ /* Require the GCC-RPM-XO clock to be registered first */
+ xo_clk = devm_clk_get(&pdev->dev, "xo");
+ if (IS_ERR(xo_clk)) {
+ if (PTR_ERR(xo_clk) != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Unable to get xo clock\n");
+ return PTR_ERR(xo_clk);
+ }
+
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cc_base");
if (!res) {
dev_err(&pdev->dev, "Register base not defined\n");
diff --git a/drivers/clk/qcom/gcc-sdxpoorwills.c b/drivers/clk/qcom/gcc-sdxpoorwills.c
index 696d7fb..c6e8faa 100644
--- a/drivers/clk/qcom/gcc-sdxpoorwills.c
+++ b/drivers/clk/qcom/gcc-sdxpoorwills.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 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
@@ -1316,33 +1316,6 @@
},
};
-static struct clk_branch gcc_mss_cfg_ahb_clk = {
- .halt_reg = 0x40000,
- .halt_check = BRANCH_HALT,
- .hwcg_reg = 0x40000,
- .hwcg_bit = 1,
- .clkr = {
- .enable_reg = 0x40000,
- .enable_mask = BIT(0),
- .hw.init = &(struct clk_init_data){
- .name = "gcc_mss_cfg_ahb_clk",
- .ops = &clk_branch2_ops,
- },
- },
-};
-
-static struct clk_gate2 gcc_mss_gpll0_div_clk_src = {
- .udelay = 500,
- .clkr = {
- .enable_reg = 0x6d004,
- .enable_mask = BIT(17),
- .hw.init = &(struct clk_init_data){
- .name = "gcc_mss_gpll0_div_clk_src",
- .ops = &clk_gate2_ops,
- },
- },
-};
-
static struct clk_branch gcc_pcie_0_clkref_clk = {
.halt_reg = 0x88004,
.halt_check = BRANCH_HALT,
@@ -1794,8 +1767,6 @@
[GCC_GP2_CLK_SRC] = &gcc_gp2_clk_src.clkr,
[GCC_GP3_CLK] = &gcc_gp3_clk.clkr,
[GCC_GP3_CLK_SRC] = &gcc_gp3_clk_src.clkr,
- [GCC_MSS_CFG_AHB_CLK] = &gcc_mss_cfg_ahb_clk.clkr,
- [GCC_MSS_GPLL0_DIV_CLK_SRC] = &gcc_mss_gpll0_div_clk_src.clkr,
[GCC_PCIE_0_CLKREF_CLK] = &gcc_pcie_0_clkref_clk.clkr,
[GCC_PCIE_AUX_CLK] = &gcc_pcie_aux_clk.clkr,
[GCC_PCIE_AUX_PHY_CLK_SRC] = &gcc_pcie_aux_phy_clk_src.clkr,
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 84b6efe..cd6d307 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -305,6 +305,14 @@
This must be disabled for hardware validation purposes to detect any
hardware anomalies of missing events.
+config ARM_ARCH_TIMER_VCT_ACCESS
+ bool "Support for ARM architected timer virtual counter access in userspace"
+ default !ARM64
+ depends on ARM_ARCH_TIMER
+ help
+ This option enables support for reading the ARM architected timer's
+ virtual counter in userspace.
+
config FSL_ERRATUM_A008585
bool "Workaround for Freescale/NXP Erratum A-008585"
default y
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 4a4ee0f..5aa9914 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -443,13 +443,12 @@
{
u32 cntkctl = arch_timer_get_cntkctl();
- /* Disable user access to the timers */
+ /* 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);
-
- cntkctl |= ARCH_TIMER_USR_PCT_ACCESS_EN;
+ | ARCH_TIMER_VIRT_EVT_EN
+ | ARCH_TIMER_USR_PCT_ACCESS_EN);
/* Enable user access to the virtual counter */
if (IS_ENABLED(CONFIG_ARM_ARCH_TIMER_VCT_ACCESS))
diff --git a/drivers/cpufreq/qcom-cpufreq.c b/drivers/cpufreq/qcom-cpufreq.c
index 90fac32..8a5ad70 100644
--- a/drivers/cpufreq/qcom-cpufreq.c
+++ b/drivers/cpufreq/qcom-cpufreq.c
@@ -27,10 +27,13 @@
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/cpu_cooling.h>
#include <trace/events/power.h>
static DEFINE_MUTEX(l2bw_lock);
+static struct thermal_cooling_device *cdev[NR_CPUS];
static struct clk *cpu_clk[NR_CPUS];
static struct clk *l2_clk;
static DEFINE_PER_CPU(struct cpufreq_frequency_table *, freq_table);
@@ -308,6 +311,52 @@
NULL,
};
+static void msm_cpufreq_ready(struct cpufreq_policy *policy)
+{
+ struct device_node *np, *lmh_node;
+ unsigned int cpu = 0;
+
+ if (cdev[policy->cpu])
+ return;
+
+ np = of_cpu_device_node_get(policy->cpu);
+ if (WARN_ON(!np))
+ return;
+
+ /*
+ * For now, just loading the cooling device;
+ * thermal DT code takes care of matching them.
+ */
+ if (of_find_property(np, "#cooling-cells", NULL)) {
+ lmh_node = of_parse_phandle(np, "qcom,lmh-dcvs", 0);
+ if (lmh_node) {
+ of_node_put(lmh_node);
+ goto ready_exit;
+ }
+
+ for_each_cpu(cpu, policy->related_cpus) {
+ cpumask_t cpu_mask = CPU_MASK_NONE;
+
+ of_node_put(np);
+ np = of_cpu_device_node_get(cpu);
+ if (WARN_ON(!np))
+ return;
+
+ cpumask_set_cpu(cpu, &cpu_mask);
+ cdev[cpu] = of_cpufreq_cooling_register(np, &cpu_mask);
+ if (IS_ERR(cdev[cpu])) {
+ pr_err(
+ "running cpufreq for CPU%d without cooling dev: %ld\n",
+ cpu, PTR_ERR(cdev[cpu]));
+ cdev[cpu] = NULL;
+ }
+ }
+ }
+
+ready_exit:
+ of_node_put(np);
+}
+
static struct cpufreq_driver msm_cpufreq_driver = {
/* lps calculations are handled here. */
.flags = CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
@@ -318,6 +367,7 @@
.get = msm_cpufreq_get_freq,
.name = "msm",
.attr = msm_freq_attr,
+ .ready = msm_cpufreq_ready,
};
static struct cpufreq_frequency_table *cpufreq_parse_dt(struct device *dev,
diff --git a/drivers/crypto/n2_core.c b/drivers/crypto/n2_core.c
index c5aac25..b365ad7 100644
--- a/drivers/crypto/n2_core.c
+++ b/drivers/crypto/n2_core.c
@@ -1620,6 +1620,7 @@
CWQ_ENTRY_SIZE, 0, NULL);
if (!queue_cache[HV_NCS_QTYPE_CWQ - 1]) {
kmem_cache_destroy(queue_cache[HV_NCS_QTYPE_MAU - 1]);
+ queue_cache[HV_NCS_QTYPE_MAU - 1] = NULL;
return -ENOMEM;
}
return 0;
@@ -1629,6 +1630,8 @@
{
kmem_cache_destroy(queue_cache[HV_NCS_QTYPE_MAU - 1]);
kmem_cache_destroy(queue_cache[HV_NCS_QTYPE_CWQ - 1]);
+ queue_cache[HV_NCS_QTYPE_MAU - 1] = NULL;
+ queue_cache[HV_NCS_QTYPE_CWQ - 1] = NULL;
}
static int spu_queue_register(struct spu_queue *p, unsigned long q_type)
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 006f723..cb54b5f 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, 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
@@ -71,6 +71,8 @@
struct completion video_comp;
bool orientation;
+ bool power_on;
+
atomic_t aborted;
u32 pixel_rate;
@@ -128,6 +130,11 @@
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+ if (!ctrl->power_on || atomic_read(&ctrl->aborted)) {
+ pr_err("CTRL off, return\n");
+ return;
+ }
+
reinit_completion(&ctrl->idle_comp);
dp_ctrl_state_ctrl(ctrl, ST_PUSH_IDLE);
@@ -833,7 +840,7 @@
tries = 0;
old_v_level = ctrl->link->phy_params.v_level;
- while (1) {
+ while (!atomic_read(&ctrl->aborted)) {
drm_dp_link_train_clock_recovery_delay(ctrl->panel->dpcd);
ret = dp_ctrl_read_link_status(ctrl, link_status);
@@ -960,7 +967,7 @@
ret = -EINVAL;
break;
}
- } while (1);
+ } while (!atomic_read(&ctrl->aborted));
return ret;
}
@@ -1180,6 +1187,11 @@
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+ if (!ctrl->power_on || atomic_read(&ctrl->aborted)) {
+ pr_err("CTRL off, return\n");
+ return -EINVAL;
+ }
+
ctrl->dp_ctrl.push_idle(&ctrl->dp_ctrl);
ctrl->dp_ctrl.reset(&ctrl->dp_ctrl);
@@ -1341,7 +1353,6 @@
atomic_set(&ctrl->aborted, 0);
rate = ctrl->panel->link_info.rate;
- ctrl->power->clk_enable(ctrl->power, DP_CORE_PM, true);
ctrl->catalog->hpd_config(ctrl->catalog, true);
if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
@@ -1396,6 +1407,7 @@
if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN)
dp_ctrl_send_phy_test_pattern(ctrl);
+ ctrl->power_on = true;
pr_debug("End-\n");
end:
@@ -1419,6 +1431,7 @@
dp_ctrl_disable_mainlink_clocks(ctrl);
+ ctrl->power_on = false;
pr_debug("DP off done\n");
}
diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c
index 0b3d903..054b3c8 100644
--- a/drivers/gpu/drm/msm/dp/dp_debug.c
+++ b/drivers/gpu/drm/msm/dp/dp_debug.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 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
@@ -665,6 +665,8 @@
pr_err("invalid input\n");
len = -EINVAL;
}
+
+ debug->panel->setup_hdr(debug->panel, &c_state->hdr_meta);
end:
return len;
}
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index f2c0a0e..d19058f 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 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
@@ -22,6 +22,8 @@
#include <linux/of_irq.h>
#include <linux/hdcp_qseecom.h>
+#include "sde_connector.h"
+
#include "msm_drv.h"
#include "dp_usbpd.h"
#include "dp_parser.h"
@@ -87,11 +89,12 @@
struct workqueue_struct *wq;
struct delayed_work hdcp_cb_work;
- struct work_struct connect_work;
+ struct delayed_work connect_work;
struct work_struct attention_work;
struct mutex hdcp_mutex;
struct mutex session_lock;
int hdcp_status;
+ unsigned long audio_status;
};
static const struct of_device_id dp_dt_match[] = {
@@ -99,6 +102,11 @@
{}
};
+static bool dp_display_framework_ready(struct dp_display_private *dp)
+{
+ return dp->dp_display.post_open ? false : true;
+}
+
static inline bool dp_display_is_hdcp_enabled(struct dp_display_private *dp)
{
return dp->hdcp.feature_enabled &&
@@ -448,31 +456,30 @@
}
/* if cable is already connected, send notification */
- if (dp_display->is_connected)
- dp_display_send_hpd_event(dp);
+ if (dp->usbpd->hpd_high)
+ queue_delayed_work(dp->wq, &dp->connect_work, HZ * 10);
else
dp_display->post_open = NULL;
-
}
static int dp_display_send_hpd_notification(struct dp_display_private *dp,
bool hpd)
{
+ u32 timeout_sec;
+
dp->dp_display.is_connected = hpd;
- /* in case, framework is not yet up, don't notify hpd */
- if (dp->dp_display.post_open)
- return 0;
+ if (dp_display_framework_ready(dp))
+ timeout_sec = 5;
+ else
+ timeout_sec = 10;
reinit_completion(&dp->notification_comp);
dp_display_send_hpd_event(dp);
- if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 5)) {
+ if (!wait_for_completion_timeout(&dp->notification_comp,
+ HZ * timeout_sec)) {
pr_warn("%s timeout\n", hpd ? "connect" : "disconnect");
- /* cancel any pending request */
- dp->ctrl->abort(dp->ctrl);
- dp->aux->abort(dp->aux);
-
return -EINVAL;
}
@@ -497,26 +504,21 @@
rc = dp->panel->read_sink_caps(dp->panel,
dp->dp_display.connector, dp->usbpd->multi_func);
if (rc) {
- if (rc == -ETIMEDOUT) {
- pr_err("Sink cap read failed, skip notification\n");
+ /*
+ * ETIMEDOUT --> cable may have been removed
+ * ENOTCONN --> no downstream device connected
+ */
+ if (rc == -ETIMEDOUT || rc == -ENOTCONN)
goto end;
- } else {
+ else
goto notify;
- }
- }
-
- dp->link->process_request(dp->link);
-
- if (dp_display_is_sink_count_zero(dp)) {
- pr_debug("no downstream devices connected\n");
- rc = -EINVAL;
- goto end;
}
edid = dp->panel->edid_ctrl->edid;
dp->audio_supported = drm_detect_monitor_audio(edid);
+ dp->link->process_request(dp->link);
dp->panel->handle_sink_request(dp->panel);
dp->dp_display.max_pclk_khz = dp->parser->max_pclk_khz;
@@ -573,9 +575,9 @@
if (dp->audio_supported)
dp->audio->off(dp->audio);
- rc = dp_display_send_hpd_notification(dp, false);
+ dp->audio_status = -ENODEV;
- dp->aux->deinit(dp->aux);
+ rc = dp_display_send_hpd_notification(dp, false);
dp->panel->video_test = false;
@@ -602,8 +604,9 @@
dp_display_host_init(dp);
- if (dp->usbpd->hpd_high)
- queue_work(dp->wq, &dp->connect_work);
+ /* check for hpd high and framework ready */
+ if (dp->usbpd->hpd_high && dp_display_framework_ready(dp))
+ queue_delayed_work(dp->wq, &dp->connect_work, 0);
end:
return rc;
}
@@ -620,6 +623,8 @@
dp->ctrl->push_idle(dp->ctrl);
dp->ctrl->off(dp->ctrl);
+ dp->panel->deinit(dp->panel);
+ dp->aux->deinit(dp->aux);
dp->power_on = false;
}
@@ -667,6 +672,7 @@
dp->aux->abort(dp->aux);
/* wait for idle state */
+ cancel_delayed_work(&dp->connect_work);
flush_workqueue(dp->wq);
dp_display_handle_disconnect(dp);
@@ -678,13 +684,13 @@
{
mutex_lock(&dp->audio->ops_lock);
- if (dp->audio_supported)
+ if (dp->audio_supported && !IS_ERR_VALUE(dp->audio_status))
dp->audio->off(dp->audio);
dp->ctrl->link_maintenance(dp->ctrl);
- if (dp->audio_supported)
- dp->audio->on(dp->audio);
+ if (dp->audio_supported && !IS_ERR_VALUE(dp->audio_status))
+ dp->audio_status = dp->audio->on(dp->audio);
mutex_unlock(&dp->audio->ops_lock);
}
@@ -707,7 +713,7 @@
return;
}
- queue_work(dp->wq, &dp->connect_work);
+ queue_delayed_work(dp->wq, &dp->connect_work, 0);
return;
}
@@ -753,17 +759,19 @@
return -ENODEV;
}
- if (dp->usbpd->hpd_irq && dp->usbpd->hpd_high) {
+ if (dp->usbpd->hpd_irq && dp->usbpd->hpd_high &&
+ dp->power_on) {
dp->link->process_request(dp->link);
queue_work(dp->wq, &dp->attention_work);
} else if (dp->usbpd->hpd_high) {
- queue_work(dp->wq, &dp->connect_work);
+ queue_delayed_work(dp->wq, &dp->connect_work, 0);
} else {
/* cancel any pending request */
dp->ctrl->abort(dp->ctrl);
dp->aux->abort(dp->aux);
/* wait for idle state */
+ cancel_delayed_work(&dp->connect_work);
flush_workqueue(dp->wq);
dp_display_handle_disconnect(dp);
@@ -774,7 +782,8 @@
static void dp_display_connect_work(struct work_struct *work)
{
- struct dp_display_private *dp = container_of(work,
+ struct delayed_work *dw = to_delayed_work(work);
+ struct dp_display_private *dp = container_of(dw,
struct dp_display_private, connect_work);
if (dp->dp_display.is_connected) {
@@ -1070,7 +1079,7 @@
if (dp->audio_supported) {
dp->audio->bw_code = dp->link->link_params.bw_code;
dp->audio->lane_count = dp->link->link_params.lane_count;
- dp->audio->on(dp->audio);
+ dp->audio_status = dp->audio->on(dp->audio);
}
dp_display_update_hdcp_info(dp);
@@ -1081,6 +1090,8 @@
dp->hdcp_status = HDCP_STATE_AUTHENTICATING;
queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ / 2);
}
+
+ dp->panel->setup_hdr(dp->panel, NULL);
end:
/* clear framework event notifier */
dp_display->post_open = NULL;
@@ -1125,6 +1136,8 @@
static int dp_display_disable(struct dp_display *dp_display)
{
struct dp_display_private *dp;
+ struct drm_connector *connector;
+ struct sde_connector_state *c_state;
if (!dp_display) {
pr_err("invalid input\n");
@@ -1132,6 +1145,8 @@
}
dp = container_of(dp_display, struct dp_display_private, dp_display);
+ connector = dp->dp_display.connector;
+ c_state = to_sde_connector_state(connector->state);
mutex_lock(&dp->session_lock);
@@ -1142,6 +1157,15 @@
dp->ctrl->off(dp->ctrl);
dp->panel->deinit(dp->panel);
+ dp->aux->deinit(dp->aux);
+
+ connector->hdr_eotf = 0;
+ connector->hdr_metadata_type_one = 0;
+ connector->hdr_max_luminance = 0;
+ connector->hdr_avg_luminance = 0;
+ connector->hdr_min_luminance = 0;
+
+ memset(&c_state->hdr_meta, 0, sizeof(c_state->hdr_meta));
dp->power_on = false;
@@ -1252,8 +1276,7 @@
return ret;
}
-
-static int dp_display_pre_kickoff(struct dp_display *dp_display,
+static int dp_display_config_hdr(struct dp_display *dp_display,
struct drm_msm_ext_hdr_metadata *hdr)
{
int rc = 0;
@@ -1266,8 +1289,7 @@
dp = container_of(dp_display, struct dp_display_private, dp_display);
- if (hdr->hdr_supported && dp->panel->hdr_supported(dp->panel))
- rc = dp->panel->setup_hdr(dp->panel, hdr);
+ rc = dp->panel->setup_hdr(dp->panel, hdr);
return rc;
}
@@ -1281,7 +1303,7 @@
}
INIT_DELAYED_WORK(&dp->hdcp_cb_work, dp_display_hdcp_cb_work);
- INIT_WORK(&dp->connect_work, dp_display_connect_work);
+ INIT_DELAYED_WORK(&dp->connect_work, dp_display_connect_work);
INIT_WORK(&dp->attention_work, dp_display_attention_work);
return 0;
@@ -1308,6 +1330,7 @@
dp->pdev = pdev;
dp->name = "drm_dp";
+ dp->audio_status = -ENODEV;
rc = dp_display_create_workqueue(dp);
if (rc) {
@@ -1332,7 +1355,7 @@
g_dp_display->get_debug = dp_get_debug;
g_dp_display->post_open = dp_display_post_open;
g_dp_display->post_init = dp_display_post_init;
- g_dp_display->pre_kickoff = dp_display_pre_kickoff;
+ g_dp_display->config_hdr = dp_display_config_hdr;
rc = component_add(&pdev->dev, &dp_display_comp_ops);
if (rc) {
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index c55e6c8..266de5f 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 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
@@ -43,7 +43,7 @@
int (*request_irq)(struct dp_display *dp_display);
struct dp_debug *(*get_debug)(struct dp_display *dp_display);
void (*post_open)(struct dp_display *dp_display);
- int (*pre_kickoff)(struct dp_display *dp_display,
+ int (*config_hdr)(struct dp_display *dp_display,
struct drm_msm_ext_hdr_metadata *hdr_meta);
void (*post_init)(struct dp_display *dp_display);
};
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index 7746b8e..b834230 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 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
@@ -276,18 +276,17 @@
.mode_set = dp_bridge_mode_set,
};
-int dp_connector_pre_kickoff(struct drm_connector *connector,
- void *display,
- struct msm_display_kickoff_params *params)
+int dp_connector_config_hdr(void *display,
+ struct sde_connector_state *c_state)
{
struct dp_display *dp = display;
- if (!connector || !display || !params) {
+ if (!display || !c_state) {
pr_err("invalid params\n");
return -EINVAL;
}
- return dp->pre_kickoff(dp, params->hdr_meta);
+ return dp->config_hdr(dp, &c_state->hdr_meta);
}
int dp_connector_post_init(struct drm_connector *connector, void *display)
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h
index 89b0a7e..3ca10c2 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_drm.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 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
@@ -32,15 +32,13 @@
};
/**
- * dp_connector_pre_kickoff - callback to perform pre kickoff initialization
- * @connector: Pointer to drm connector structure
+ * dp_connector_config_hdr - callback to configure HDR
* @display: Pointer to private display handle
- * @params: Pointer to kickoff parameters
+ * @c_state: connect state data
* Returns: Zero on success
*/
-int dp_connector_pre_kickoff(struct drm_connector *connector,
- void *display,
- struct msm_display_kickoff_params *params);
+int dp_connector_config_hdr(void *display,
+ struct sde_connector_state *c_state);
/**
* dp_connector_post_init - callback to perform additional initialization steps
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index 0401760..e0aedc0 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, 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
@@ -309,12 +309,14 @@
static int dp_panel_read_sink_caps(struct dp_panel *dp_panel,
struct drm_connector *connector, bool multi_func)
{
- int rc = 0;
+ int rc = 0, rlen, count, downstream_ports;
+ const int count_len = 1;
struct dp_panel_private *panel;
if (!dp_panel || !connector) {
pr_err("invalid input\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto end;
}
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
@@ -327,19 +329,35 @@
dp_panel->max_bw_code)) {
if ((rc == -ETIMEDOUT) || (rc == -ENODEV)) {
pr_err("DPCD read failed, return early\n");
- return rc;
+ goto end;
}
pr_err("panel dpcd read failed/incorrect, set default params\n");
dp_panel_set_default_link_params(dp_panel);
}
+ downstream_ports = dp_panel->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+ DP_DWN_STRM_PORT_PRESENT;
+
+ if (downstream_ports) {
+ rlen = drm_dp_dpcd_read(panel->aux->drm_aux, DP_SINK_COUNT,
+ &count, count_len);
+ if (rlen == count_len) {
+ count = DP_GET_SINK_COUNT(count);
+ if (!count) {
+ pr_err("no downstream ports connected\n");
+ rc = -ENOTCONN;
+ goto end;
+ }
+ }
+ }
+
rc = dp_panel_read_edid(dp_panel, connector);
if (rc) {
pr_err("panel edid read failed, set failsafe mode\n");
return rc;
}
-
- return 0;
+end:
+ return rc;
}
static u32 dp_panel_get_supported_bpp(struct dp_panel *dp_panel,
@@ -648,6 +666,7 @@
{
int rc = 0;
struct dp_panel_private *panel;
+ struct dp_catalog_hdr_data *hdr;
if (!dp_panel) {
pr_err("invalid input\n");
@@ -655,11 +674,13 @@
}
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+ hdr = &panel->catalog->hdr_data;
if (!panel->custom_edid)
sde_free_edid((void **)&dp_panel->edid_ctrl);
memset(&dp_panel->pinfo, 0, sizeof(dp_panel->pinfo));
+ memset(&hdr->hdr_meta, 0, sizeof(hdr->hdr_meta));
panel->panel_on = false;
return rc;
@@ -706,30 +727,6 @@
(panel->minor >= 4 || panel->vscext_supported);
}
-static bool dp_panel_is_validate_hdr_state(struct dp_panel_private *panel,
- struct drm_msm_ext_hdr_metadata *hdr_meta)
-{
- struct drm_msm_ext_hdr_metadata *panel_hdr_meta =
- &panel->catalog->hdr_data.hdr_meta;
-
- if (!hdr_meta)
- goto end;
-
- /* bail out if HDR not active */
- if (hdr_meta->hdr_state == HDR_DISABLED &&
- panel->hdr_state == HDR_DISABLED)
- goto end;
-
- /* bail out if same meta data is received */
- if (hdr_meta->hdr_state == HDR_ENABLED &&
- panel_hdr_meta->eotf == hdr_meta->eotf)
- goto end;
-
- return true;
-end:
- return false;
-}
-
static int dp_panel_setup_hdr(struct dp_panel *dp_panel,
struct drm_msm_ext_hdr_metadata *hdr_meta)
{
@@ -744,14 +741,18 @@
}
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+ hdr = &panel->catalog->hdr_data;
- if (!dp_panel_is_validate_hdr_state(panel, hdr_meta))
- goto end;
+ /* use cached meta data in case meta data not provided */
+ if (!hdr_meta) {
+ if (hdr->hdr_meta.hdr_state)
+ goto cached;
+ else
+ goto end;
+ }
panel->hdr_state = hdr_meta->hdr_state;
- hdr = &panel->catalog->hdr_data;
-
hdr->ext_header_byte0 = 0x00;
hdr->ext_header_byte1 = 0x04;
hdr->ext_header_byte2 = 0x1F;
@@ -786,8 +787,9 @@
memcpy(&hdr->hdr_meta, hdr_meta, sizeof(hdr->hdr_meta));
else
memset(&hdr->hdr_meta, 0, sizeof(hdr->hdr_meta));
-
- panel->catalog->config_hdr(panel->catalog, panel->hdr_state);
+cached:
+ if (panel->panel_on)
+ panel->catalog->config_hdr(panel->catalog, panel->hdr_state);
end:
return rc;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index d5c4386..df0aad5e 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -939,34 +939,38 @@
struct sde_connector_state *c_state,
void *usr_ptr)
{
+ int rc = 0;
struct drm_connector *connector;
struct drm_msm_ext_hdr_metadata *hdr_meta;
int i;
if (!c_conn || !c_state) {
SDE_ERROR_CONN(c_conn, "invalid args\n");
- return -EINVAL;
+ rc = -EINVAL;
+ goto end;
}
connector = &c_conn->base;
if (!connector->hdr_supported) {
SDE_ERROR_CONN(c_conn, "sink doesn't support HDR\n");
- return -ENOTSUPP;
+ rc = -ENOTSUPP;
+ goto end;
}
memset(&c_state->hdr_meta, 0, sizeof(c_state->hdr_meta));
if (!usr_ptr) {
SDE_DEBUG_CONN(c_conn, "hdr metadata cleared\n");
- return 0;
+ goto end;
}
if (copy_from_user(&c_state->hdr_meta,
(void __user *)usr_ptr,
sizeof(*hdr_meta))) {
SDE_ERROR_CONN(c_conn, "failed to copy hdr metadata\n");
- return -EFAULT;
+ rc = -EFAULT;
+ goto end;
}
hdr_meta = &c_state->hdr_meta;
@@ -989,7 +993,10 @@
hdr_meta->display_primaries_y[i]);
}
- return 0;
+ if (c_conn->ops.config_hdr)
+ rc = c_conn->ops.config_hdr(c_conn->display, c_state);
+end:
+ return rc;
}
static int sde_connector_atomic_set_property(struct drm_connector *connector,
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index 7cf09b7..9c37869 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -241,6 +241,16 @@
*/
int (*cmd_transfer)(void *display, const char *cmd_buf,
u32 cmd_buf_len);
+
+ /**
+ * config_hdr - configure HDR
+ * @display: Pointer to private display handle
+ * @c_state: Pointer to connector state
+ * Returns: Zero on success, negative error code for failures
+ */
+ int (*config_hdr)(void *display,
+ struct sde_connector_state *c_state);
+
};
/**
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.c b/drivers/gpu/drm/msm/sde/sde_core_irq.c
index a6f22c9..442104b 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 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
@@ -513,6 +513,7 @@
struct msm_drm_private *priv;
int i;
int rc;
+ unsigned long irq_flags;
if (!sde_kms) {
SDE_ERROR("invalid sde_kms\n");
@@ -543,6 +544,7 @@
sde_disable_all_irqs(sde_kms);
sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
+ spin_lock_irqsave(&sde_kms->irq_obj.cb_lock, irq_flags);
kfree(sde_kms->irq_obj.irq_cb_tbl);
kfree(sde_kms->irq_obj.enable_counts);
kfree(sde_kms->irq_obj.irq_counts);
@@ -550,6 +552,7 @@
sde_kms->irq_obj.enable_counts = NULL;
sde_kms->irq_obj.irq_counts = NULL;
sde_kms->irq_obj.total_irqs = 0;
+ spin_unlock_irqrestore(&sde_kms->irq_obj.cb_lock, irq_flags);
}
static void sde_core_irq_mask(struct irq_data *irqd)
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index b3eb101..73e2af0 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -3276,7 +3276,8 @@
* smmu state is attached,
*/
if ((smmu_state->state != DETACHED) &&
- (smmu_state->state != DETACH_ALL_REQ))
+ (smmu_state->state != DETACH_ALL_REQ) &&
+ sde_crtc->enabled)
sde_cp_crtc_apply_properties(crtc);
/*
@@ -4487,28 +4488,41 @@
}
static int _sde_crtc_excl_rect_overlap_check(struct plane_state pstates[],
- int cnt, int curr_cnt, struct sde_rect *excl_rect, int z_pos)
+ int cnt, int curr_cnt, struct sde_rect *excl_rect)
{
struct sde_rect dst_rect, intersect;
int i, rc = -EINVAL;
const struct drm_plane_state *pstate;
- /* start checking from next plane */
- for (i = curr_cnt; i < cnt; i++) {
+ for (i = 0; i < cnt; i++) {
+ if (i == curr_cnt)
+ continue;
+
pstate = pstates[i].drm_pstate;
POPULATE_RECT(&dst_rect, pstate->crtc_x, pstate->crtc_y,
pstate->crtc_w, pstate->crtc_h, false);
sde_kms_rect_intersect(&dst_rect, excl_rect, &intersect);
+ /* complete intersection of excl_rect is required */
if (intersect.w == excl_rect->w && intersect.h == excl_rect->h
- /* next plane may be on same z-order */
- && z_pos != pstates[i].stage) {
+ /* intersecting rect should be in another z_order */
+ && pstates[curr_cnt].stage != pstates[i].stage) {
rc = 0;
goto end;
}
}
- SDE_ERROR("excl rect does not find top overlapping rect\n");
+ SDE_ERROR(
+ "no overlapping rect for [%d] z_pos:%d, excl_rect:{%d,%d,%d,%d}\n",
+ i, pstates[curr_cnt].stage,
+ excl_rect->x, excl_rect->y, excl_rect->w, excl_rect->h);
+ for (i = 0; i < cnt; i++) {
+ pstate = pstates[i].drm_pstate;
+ SDE_ERROR("[%d] p:%d, z_pos:%d, src:{%d,%d,%d,%d}\n",
+ i, pstate->plane->base.id, pstates[i].stage,
+ pstate->crtc_x, pstate->crtc_y,
+ pstate->crtc_w, pstate->crtc_h);
+ }
end:
return rc;
}
@@ -4550,9 +4564,9 @@
pstate = pstates[i].drm_pstate;
sde_pstate = to_sde_plane_state(pstate);
if (sde_pstate->excl_rect.w && sde_pstate->excl_rect.h) {
- /* check overlap on all top z-order */
+ /* check overlap on any other z-order */
rc = _sde_crtc_excl_rect_overlap_check(pstates, cnt,
- i + 1, &sde_pstate->excl_rect, pstates[i].stage);
+ i, &sde_pstate->excl_rect);
if (rc)
goto end;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 3b5e3f5..92ab669 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <robdclark@gmail.com>
*
@@ -3739,6 +3739,7 @@
SDE_ATRACE_BEGIN("enc_prepare_for_kickoff");
for (i = 0; i < sde_enc->num_phys_encs; i++) {
phys = sde_enc->phys_encs[i];
+ params->is_primary = sde_enc->disp_info.is_primary;
if (phys) {
if (phys->ops.prepare_for_kickoff) {
rc = phys->ops.prepare_for_kickoff(
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index 8038eb6..2c84e20 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <robdclark@gmail.com>
*
@@ -54,11 +54,13 @@
/**
* sde_encoder_kickoff_params - info encoder requires at kickoff
* @inline_rotate_prefill: number of lines to prefill for inline rotation
+ * @is_primary: set to true if the display is primary display
* @affected_displays: bitmask, bit set means the ROI of the commit lies within
* the bounds of the physical display at the bit index
*/
struct sde_encoder_kickoff_params {
u32 inline_rotate_prefill;
+ u32 is_primary;
unsigned long affected_displays;
};
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index 4a15e6f..69dc385 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -245,9 +245,10 @@
* HW layer requires VSYNC counter of first pixel of tgt VFP line.
* @phys_enc: Pointer to physical encoder
* @rot_fetch_lines: number of line to prefill, or 0 to disable
+ * @is_primary: set true if the display is primary display
*/
static void programmable_rot_fetch_config(struct sde_encoder_phys *phys_enc,
- u32 rot_fetch_lines)
+ u32 rot_fetch_lines, u32 is_primary)
{
struct sde_encoder_phys_vid *vid_enc =
to_sde_encoder_phys_vid(phys_enc);
@@ -264,7 +265,8 @@
!phys_enc->hw_ctl->ops.get_bitmask_intf ||
!phys_enc->hw_ctl->ops.update_pending_flush ||
!vid_enc->hw_intf->ops.setup_rot_start ||
- !phys_enc->sde_kms)
+ !phys_enc->sde_kms ||
+ !is_primary)
return;
timing = &vid_enc->timing_params;
@@ -873,7 +875,8 @@
vid_enc->error_count = 0;
}
- programmable_rot_fetch_config(phys_enc, params->inline_rotate_prefill);
+ programmable_rot_fetch_config(phys_enc,
+ params->inline_rotate_prefill, params->is_primary);
return rc;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index 545ed65..cdc6a9c 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 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
@@ -1681,6 +1681,8 @@
if (sde_cfg->has_wb_ubwc)
set_bit(SDE_WB_UBWC, &wb->features);
+ set_bit(SDE_WB_XY_ROI_OFFSET, &wb->features);
+
for (j = 0; j < sde_cfg->mdp_count; j++) {
sde_cfg->mdp[j].clk_ctrls[wb->clk_ctrl].reg_off =
PROP_BITVALUE_ACCESS(prop_value,
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 2b0aa37..5d3835c 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -949,7 +949,7 @@
.get_mode_info = dp_connector_get_mode_info,
.post_open = dp_connector_post_open,
.check_status = NULL,
- .pre_kickoff = dp_connector_pre_kickoff,
+ .config_hdr = dp_connector_config_hdr,
.cmd_transfer = NULL,
};
struct msm_display_info info;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index fefb9d9..81f5a55 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -2729,6 +2729,8 @@
}
view_type = vmw_view_cmd_to_type(header->id);
+ if (view_type == vmw_view_max)
+ return -EINVAL;
cmd = container_of(header, typeof(*cmd), header);
ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface,
user_surface_converter,
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index c01640c..d8b347d 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -364,7 +364,7 @@
{ A6XX_CP_AHB_CNTL, 0x0 },
};
-static struct reg_list_pair a615_ifpc_pwrup_reglist[] = {
+static struct reg_list_pair a615_pwrup_reglist[] = {
{ A6XX_UCHE_GBIF_GX_CONFIG, 0x0 },
};
@@ -604,7 +604,6 @@
uint32_t i;
struct cpu_gpu_lock *lock;
struct reg_list_pair *r;
- uint16_t a615_list_size = 0;
/* Set up the register values */
for (i = 0; i < ARRAY_SIZE(a6xx_ifpc_pwrup_reglist); i++) {
@@ -617,19 +616,6 @@
kgsl_regread(KGSL_DEVICE(adreno_dev), r->offset, &r->val);
}
- if (adreno_is_a615(adreno_dev)) {
- for (i = 0; i < ARRAY_SIZE(a615_ifpc_pwrup_reglist); i++) {
- r = &a615_ifpc_pwrup_reglist[i];
- kgsl_regread(KGSL_DEVICE(adreno_dev),
- r->offset, &r->val);
- }
-
- a615_list_size = sizeof(a615_ifpc_pwrup_reglist);
-
- memcpy(adreno_dev->pwrup_reglist.hostptr + sizeof(*lock),
- a615_ifpc_pwrup_reglist, a615_list_size);
- }
-
lock = (struct cpu_gpu_lock *) adreno_dev->pwrup_reglist.hostptr;
lock->flag_ucode = 0;
lock->flag_kmd = 0;
@@ -648,16 +634,29 @@
* of the static IFPC-only register list.
*/
lock->list_length = (sizeof(a6xx_ifpc_pwrup_reglist) +
- sizeof(a6xx_pwrup_reglist) + a615_list_size) >> 2;
- lock->list_offset = (sizeof(a6xx_ifpc_pwrup_reglist) +
- a615_list_size) >> 2;
+ sizeof(a6xx_pwrup_reglist)) >> 2;
+ lock->list_offset = sizeof(a6xx_ifpc_pwrup_reglist) >> 2;
- memcpy(adreno_dev->pwrup_reglist.hostptr + sizeof(*lock)
- + a615_list_size,
+ memcpy(adreno_dev->pwrup_reglist.hostptr + sizeof(*lock),
a6xx_ifpc_pwrup_reglist, sizeof(a6xx_ifpc_pwrup_reglist));
memcpy(adreno_dev->pwrup_reglist.hostptr + sizeof(*lock)
- + sizeof(a6xx_ifpc_pwrup_reglist) + a615_list_size,
- a6xx_pwrup_reglist, sizeof(a6xx_pwrup_reglist));
+ + sizeof(a6xx_ifpc_pwrup_reglist), a6xx_pwrup_reglist,
+ sizeof(a6xx_pwrup_reglist));
+
+ if (adreno_is_a615(adreno_dev)) {
+ for (i = 0; i < ARRAY_SIZE(a615_pwrup_reglist); i++) {
+ r = &a615_pwrup_reglist[i];
+ kgsl_regread(KGSL_DEVICE(adreno_dev),
+ r->offset, &r->val);
+ }
+
+ memcpy(adreno_dev->pwrup_reglist.hostptr + sizeof(*lock)
+ + sizeof(a6xx_ifpc_pwrup_reglist)
+ + sizeof(a6xx_pwrup_reglist), a615_pwrup_reglist,
+ sizeof(a615_pwrup_reglist));
+
+ lock->list_length += sizeof(a615_pwrup_reglist);
+ }
}
/*
diff --git a/drivers/gpu/msm/adreno_a6xx_snapshot.c b/drivers/gpu/msm/adreno_a6xx_snapshot.c
index 97f2c58..afd1be5 100644
--- a/drivers/gpu/msm/adreno_a6xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a6xx_snapshot.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -376,6 +376,7 @@
A6XX_DBGBUS_CX = 0x17,
A6XX_DBGBUS_GMU_GX = 0x18,
A6XX_DBGBUS_TPFCHE = 0x19,
+ A6XX_DBGBUS_GBIF_GX = 0x1a,
A6XX_DBGBUS_GPC = 0x1d,
A6XX_DBGBUS_LARC = 0x1e,
A6XX_DBGBUS_HLSQ_SPTP = 0x1f,
@@ -1161,11 +1162,14 @@
}
header->id = block->block_id;
+ if ((block->block_id == A6XX_DBGBUS_VBIF) &&
+ adreno_has_gbif(adreno_dev))
+ header->id = A6XX_DBGBUS_GBIF_GX;
header->count = dwords * 2;
block_id = block->block_id;
/* GMU_GX data is read using the GMU_CX block id on A630 */
- if (adreno_is_a630(adreno_dev) &&
+ if ((adreno_is_a630(adreno_dev) || adreno_is_a615(adreno_dev)) &&
(block_id == A6XX_DBGBUS_GMU_GX))
block_id = A6XX_DBGBUS_GMU_CX;
@@ -1428,18 +1432,18 @@
KGSL_SNAPSHOT_SECTION_DEBUGBUS,
snapshot, a6xx_snapshot_cx_dbgc_debugbus_block,
(void *) &a6xx_cx_dbgc_debugbus_blocks[i]);
- /*
- * Get debugbus for GBIF CX part if GPU has GBIF block
- * GBIF uses exactly same ID as of VBIF so use
- * it as it is.
- */
- if (adreno_has_gbif(adreno_dev))
- kgsl_snapshot_add_section(device,
- KGSL_SNAPSHOT_SECTION_DEBUGBUS,
- snapshot,
- a6xx_snapshot_cx_dbgc_debugbus_block,
- (void *) &a6xx_vbif_debugbus_blocks);
}
+ /*
+ * Get debugbus for GBIF CX part if GPU has GBIF block
+ * GBIF uses exactly same ID as of VBIF so use
+ * it as it is.
+ */
+ if (adreno_has_gbif(adreno_dev))
+ kgsl_snapshot_add_section(device,
+ KGSL_SNAPSHOT_SECTION_DEBUGBUS,
+ snapshot,
+ a6xx_snapshot_cx_dbgc_debugbus_block,
+ (void *) &a6xx_vbif_debugbus_blocks);
}
}
diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index 66fc011..52d45bb 100644
--- a/drivers/gpu/msm/kgsl_gmu.c
+++ b/drivers/gpu/msm/kgsl_gmu.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -1307,10 +1307,13 @@
do {
if (!regulator_is_enabled(gmu->cx_gdsc))
return 0;
- cond_resched();
+ usleep_range(10, 100);
} while (!(time_after(jiffies, t)));
+ if (!regulator_is_enabled(gmu->cx_gdsc))
+ return 0;
+
dev_err(&gmu->pdev->dev, "GMU CX gdsc off timeout");
return -ETIMEDOUT;
}
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
index e0a8216..13c32eb4 100644
--- a/drivers/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -31,6 +31,7 @@
#include <linux/clockchips.h>
#include <asm/hyperv.h>
#include <asm/mshyperv.h>
+#include <asm/nospec-branch.h>
#include "hyperv_vmbus.h"
/* The one and only */
@@ -103,9 +104,10 @@
return (u64)ULLONG_MAX;
__asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8");
- __asm__ __volatile__("call *%3" : "=a" (hv_status) :
+ __asm__ __volatile__(CALL_NOSPEC :
+ "=a" (hv_status) :
"c" (control), "d" (input_address),
- "m" (hypercall_page));
+ THUNK_TARGET(hypercall_page));
return hv_status;
@@ -123,11 +125,12 @@
if (!hypercall_page)
return (u64)ULLONG_MAX;
- __asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi),
+ __asm__ __volatile__ (CALL_NOSPEC : "=d"(hv_status_hi),
"=a"(hv_status_lo) : "d" (control_hi),
"a" (control_lo), "b" (input_address_hi),
"c" (input_address_lo), "D"(output_address_hi),
- "S"(output_address_lo), "m" (hypercall_page));
+ "S"(output_address_lo),
+ THUNK_TARGET(hypercall_page));
return hv_status_lo | ((u64)hv_status_hi << 32);
#endif /* !x86_64 */
diff --git a/drivers/infiniband/hw/cxgb4/cq.c b/drivers/infiniband/hw/cxgb4/cq.c
index 19c6477..a856371 100644
--- a/drivers/infiniband/hw/cxgb4/cq.c
+++ b/drivers/infiniband/hw/cxgb4/cq.c
@@ -575,10 +575,10 @@
ret = -EAGAIN;
goto skip_cqe;
}
- if (unlikely((CQE_WRID_MSN(hw_cqe) != (wq->rq.msn)))) {
+ if (unlikely(!CQE_STATUS(hw_cqe) &&
+ CQE_WRID_MSN(hw_cqe) != wq->rq.msn)) {
t4_set_wq_in_error(wq);
- hw_cqe->header |= htonl(CQE_STATUS_V(T4_ERR_MSN));
- goto proc_cqe;
+ hw_cqe->header |= cpu_to_be32(CQE_STATUS_V(T4_ERR_MSN));
}
goto proc_cqe;
}
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index b974897..29ab814 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -992,8 +992,7 @@
return -ENOMEM;
attr->qp_state = IB_QPS_INIT;
- attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE | IB_ACCESS_REMOTE_READ |
- IB_ACCESS_REMOTE_WRITE;
+ attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE;
attr->port_num = ch->sport->port;
attr->pkey_index = 0;
diff --git a/drivers/input/misc/qpnp-power-on.c b/drivers/input/misc/qpnp-power-on.c
index febcd9c..5fa0d4b 100644
--- a/drivers/input/misc/qpnp-power-on.c
+++ b/drivers/input/misc/qpnp-power-on.c
@@ -212,6 +212,9 @@
int warm_reset_poff_type;
int hard_reset_poff_type;
int shutdown_poff_type;
+ int resin_warm_reset_type;
+ int resin_hard_reset_type;
+ int resin_shutdown_type;
u16 base;
u8 subtype;
u8 pon_ver;
@@ -219,7 +222,12 @@
u8 warm_reset_reason2;
bool is_spon;
bool store_hard_reset_reason;
+ bool resin_hard_reset_disable;
+ bool resin_shutdown_disable;
+ bool ps_hold_hard_reset_disable;
+ bool ps_hold_shutdown_disable;
bool kpdpwr_dbc_enable;
+ bool resin_pon_reset;
ktime_t kpdpwr_last_release_time;
};
@@ -478,6 +486,7 @@
enum pon_power_off_type type)
{
int rc;
+ bool disable = false;
u16 rst_en_reg;
if (pon->pon_ver == QPNP_PON_GEN1_V1)
@@ -497,10 +506,12 @@
case PON_POWER_OFF_HARD_RESET:
if (pon->hard_reset_poff_type != -EINVAL)
type = pon->hard_reset_poff_type;
+ disable = pon->ps_hold_hard_reset_disable;
break;
case PON_POWER_OFF_SHUTDOWN:
if (pon->shutdown_poff_type != -EINVAL)
type = pon->shutdown_poff_type;
+ disable = pon->ps_hold_shutdown_disable;
break;
default:
break;
@@ -513,6 +524,13 @@
rst_en_reg, rc);
/*
+ * Check if ps-hold power off configuration needs to be disabled.
+ * If yes, then return without configuring.
+ */
+ if (disable)
+ return rc;
+
+ /*
* We need 10 sleep clock cycles here. But since the clock is
* internally generated, we need to add 50% tolerance to be
* conservative.
@@ -533,7 +551,80 @@
"Unable to write to addr=%hx, rc(%d)\n",
rst_en_reg, rc);
- dev_dbg(&pon->pdev->dev, "power off type = 0x%02X\n", type);
+ dev_dbg(&pon->pdev->dev, "ps_hold power off type = 0x%02X\n", type);
+ return rc;
+}
+
+static int qpnp_resin_pon_reset_config(struct qpnp_pon *pon,
+ enum pon_power_off_type type)
+{
+ int rc;
+ bool disable = false;
+ u16 rst_en_reg;
+
+ if (pon->pon_ver == QPNP_PON_GEN1_V1)
+ rst_en_reg = QPNP_PON_RESIN_S2_CNTL(pon);
+ else
+ rst_en_reg = QPNP_PON_RESIN_S2_CNTL2(pon);
+
+ /*
+ * Based on the poweroff type set for a PON device through device tree
+ * change the type being configured into PON_RESIN_S2_CTL.
+ */
+ switch (type) {
+ case PON_POWER_OFF_WARM_RESET:
+ if (pon->resin_warm_reset_type != -EINVAL)
+ type = pon->resin_warm_reset_type;
+ break;
+ case PON_POWER_OFF_HARD_RESET:
+ if (pon->resin_hard_reset_type != -EINVAL)
+ type = pon->resin_hard_reset_type;
+ disable = pon->resin_hard_reset_disable;
+ break;
+ case PON_POWER_OFF_SHUTDOWN:
+ if (pon->resin_shutdown_type != -EINVAL)
+ type = pon->resin_shutdown_type;
+ disable = pon->resin_shutdown_disable;
+ break;
+ default:
+ break;
+ }
+
+ rc = qpnp_pon_masked_write(pon, rst_en_reg, QPNP_PON_S2_CNTL_EN, 0);
+ if (rc)
+ dev_err(&pon->pdev->dev,
+ "Unable to write to addr=%hx, rc(%d)\n",
+ rst_en_reg, rc);
+
+ /*
+ * Check if resin power off configuration needs to be disabled.
+ * If yes, then return without configuring.
+ */
+ if (disable)
+ return rc;
+
+ /*
+ * We need 10 sleep clock cycles here. But since the clock is
+ * internally generated, we need to add 50% tolerance to be
+ * conservative.
+ */
+ udelay(500);
+
+ rc = qpnp_pon_masked_write(pon, QPNP_PON_RESIN_S2_CNTL(pon),
+ QPNP_PON_S2_CNTL_TYPE_MASK, type);
+ if (rc)
+ dev_err(&pon->pdev->dev,
+ "Unable to write to addr=%x, rc(%d)\n",
+ QPNP_PON_RESIN_S2_CNTL(pon), rc);
+
+ rc = qpnp_pon_masked_write(pon, rst_en_reg, QPNP_PON_S2_CNTL_EN,
+ QPNP_PON_S2_CNTL_EN);
+ if (rc)
+ dev_err(&pon->pdev->dev,
+ "Unable to write to addr=%hx, rc(%d)\n",
+ rst_en_reg, rc);
+
+ dev_dbg(&pon->pdev->dev, "resin power off type = 0x%02X\n", type);
return rc;
}
@@ -588,6 +679,15 @@
rc);
goto out;
}
+ if (pon->resin_pon_reset) {
+ rc = qpnp_resin_pon_reset_config(pon, type);
+ if (rc) {
+ dev_err(&pon->pdev->dev,
+ "Error configuring secondary PON resin rc: %d\n",
+ rc);
+ goto out;
+ }
+ }
}
/* Set ship mode here if it has been requested */
if (!!pon_ship_mode_en) {
@@ -2337,6 +2437,69 @@
pon->shutdown_poff_type = -EINVAL;
}
+ pon->ps_hold_hard_reset_disable =
+ of_property_read_bool(pdev->dev.of_node,
+ "qcom,ps-hold-hard-reset-disable");
+ pon->ps_hold_shutdown_disable = of_property_read_bool(pdev->dev.of_node,
+ "qcom,ps-hold-shutdown-disable");
+
+
+ pon->resin_pon_reset = of_property_read_bool(pdev->dev.of_node,
+ "qcom,resin-pon-reset");
+
+ rc = of_property_read_u32(pon->pdev->dev.of_node,
+ "qcom,resin-warm-reset-type",
+ &pon->resin_warm_reset_type);
+ if (rc) {
+ if (rc != -EINVAL) {
+ dev_err(&pdev->dev, "Unable to read resin warm reset poweroff type rc: %d\n",
+ rc);
+ goto err_out;
+ }
+ pon->resin_warm_reset_type = -EINVAL;
+ } else if (pon->resin_warm_reset_type <= PON_POWER_OFF_RESERVED ||
+ pon->resin_warm_reset_type >= PON_POWER_OFF_MAX_TYPE) {
+ dev_err(&pdev->dev, "Invalid resin-warm-reset-type\n");
+ pon->resin_warm_reset_type = -EINVAL;
+ }
+
+ rc = of_property_read_u32(pon->pdev->dev.of_node,
+ "qcom,resin-hard-reset-type",
+ &pon->resin_hard_reset_type);
+ if (rc) {
+ if (rc != -EINVAL) {
+ dev_err(&pdev->dev, "Unable to read resin hard reset poweroff type rc: %d\n",
+ rc);
+ goto err_out;
+ }
+ pon->resin_hard_reset_type = -EINVAL;
+ } else if (pon->resin_hard_reset_type <= PON_POWER_OFF_RESERVED ||
+ pon->resin_hard_reset_type >= PON_POWER_OFF_MAX_TYPE) {
+ dev_err(&pdev->dev, "Invalid resin-hard-reset-type\n");
+ pon->resin_hard_reset_type = -EINVAL;
+ }
+
+ rc = of_property_read_u32(pon->pdev->dev.of_node,
+ "qcom,resin-shutdown-type",
+ &pon->resin_shutdown_type);
+ if (rc) {
+ if (rc != -EINVAL) {
+ dev_err(&pdev->dev, "Unable to read resin shutdown poweroff type rc: %d\n",
+ rc);
+ goto err_out;
+ }
+ pon->resin_shutdown_type = -EINVAL;
+ } else if (pon->resin_shutdown_type <= PON_POWER_OFF_RESERVED ||
+ pon->resin_shutdown_type >= PON_POWER_OFF_MAX_TYPE) {
+ dev_err(&pdev->dev, "Invalid resin-shutdown-type\n");
+ pon->resin_shutdown_type = -EINVAL;
+ }
+
+ pon->resin_hard_reset_disable = of_property_read_bool(pdev->dev.of_node,
+ "qcom,resin-hard-reset-disable");
+ pon->resin_shutdown_disable = of_property_read_bool(pdev->dev.of_node,
+ "qcom,resin-shutdown-disable");
+
rc = device_create_file(&pdev->dev, &dev_attr_debounce_us);
if (rc) {
dev_err(&pdev->dev, "sys file creation failed rc: %d\n", rc);
@@ -2365,7 +2528,8 @@
return 0;
err_out:
- sys_reset_dev = NULL;
+ if (sys_reset)
+ sys_reset_dev = NULL;
return rc;
}
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index cd834da..59603a5 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1609,7 +1609,7 @@
case 5:
etd->hw_version = 3;
break;
- case 6 ... 14:
+ case 6 ... 15:
etd->hw_version = 4;
break;
default:
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 36777b3..86168b9 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -11,6 +11,8 @@
if INPUT_TOUCHSCREEN
+source "drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig"
+
config TOUCHSCREEN_PROPERTIES
def_tristate INPUT
depends on INPUT
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 0caab59..7ac5a98 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -72,6 +72,7 @@
obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o
obj-$(CONFIG_TOUCHSCREEN_SURFACE3_SPI) += surface3_spi.o
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX) += synaptics_dsx/
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_v26) += synaptics_dsx_2.6/
obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig b/drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig
index b40f26e..78b995e 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/Kconfig
@@ -1,33 +1,33 @@
#
-# Synaptics DSX touchscreen driver configuration
+# Synaptics DSX v2.6 touchscreen driver configuration
#
-menuconfig TOUCHSCREEN_SYNAPTICS_DSX
- bool "Synaptics DSX touchscreen"
- default N
+menuconfig TOUCHSCREEN_SYNAPTICS_DSX_v26
+ bool "Synaptics DSX v2.6 touchscreen"
+ default y
help
Say Y here if you have a Synaptics DSX touchscreen connected
to your system.
If unsure, say N.
-if TOUCHSCREEN_SYNAPTICS_DSX
+if TOUCHSCREEN_SYNAPTICS_DSX_v26
choice
- default TOUCHSCREEN_SYNAPTICS_DSX_I2C
- prompt "Synaptics DSX bus interface"
-config TOUCHSCREEN_SYNAPTICS_DSX_I2C
+ default TOUCHSCREEN_SYNAPTICS_DSX_I2C_v26
+ prompt "Synaptics DSX v2.6 bus interface"
+config TOUCHSCREEN_SYNAPTICS_DSX_I2C_v26
bool "RMI over I2C"
depends on I2C
-config TOUCHSCREEN_SYNAPTICS_DSX_SPI
+config TOUCHSCREEN_SYNAPTICS_DSX_SPI_v26
bool "RMI over SPI"
depends on SPI_MASTER
-config TOUCHSCREEN_SYNAPTICS_DSX_RMI_HID_I2C
+config TOUCHSCREEN_SYNAPTICS_DSX_RMI_HID_I2C_v26
bool "HID over I2C"
depends on I2C
endchoice
-config TOUCHSCREEN_SYNAPTICS_DSX_CORE
- tristate "Synaptics DSX core driver module"
+config TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
+ tristate "Synaptics DSX v2.6 core driver module"
depends on I2C || SPI_MASTER
help
Say Y here to enable basic touch reporting functionality.
@@ -37,9 +37,9 @@
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_core.
-config TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV
- tristate "Synaptics DSX RMI device module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
+config TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v26
+ tristate "Synaptics DSX v2.6 RMI device module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
help
Say Y here to enable support for direct RMI register access.
@@ -48,9 +48,9 @@
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_rmi_dev.
-config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE
- tristate "Synaptics DSX firmware update module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
+config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v26
+ tristate "Synaptics DSX v2.6 firmware update module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
help
Say Y here to enable support for doing firmware update.
@@ -59,9 +59,9 @@
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_fw_update.
-config TOUCHSCREEN_SYNAPTICS_DSX_TEST_REPORTING
- tristate "Synaptics DSX test reporting module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
+config TOUCHSCREEN_SYNAPTICS_DSX_TEST_REPORTING_v26
+ tristate "Synaptics DSX v2.6 test reporting module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
help
Say Y here to enable support for retrieving production test reports.
@@ -70,9 +70,9 @@
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_test_reporting.
-config TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY
- tristate "Synaptics DSX proximity module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
+config TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY_v26
+ tristate "Synaptics DSX v2.6 proximity module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
help
Say Y here to enable support for proximity functionality.
@@ -81,9 +81,9 @@
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_proximity.
-config TOUCHSCREEN_SYNAPTICS_DSX_ACTIVE_PEN
- tristate "Synaptics DSX active pen module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
+config TOUCHSCREEN_SYNAPTICS_DSX_ACTIVE_PEN_v26
+ tristate "Synaptics DSX v2.6 active pen module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
help
Say Y here to enable support for active pen functionality.
@@ -92,9 +92,9 @@
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_active_pen.
-config TOUCHSCREEN_SYNAPTICS_DSX_GESTURE
- tristate "Synaptics DSX user defined gesture module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
+config TOUCHSCREEN_SYNAPTICS_DSX_GESTURE_v26
+ tristate "Synaptics DSX v2.6 user defined gesture module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
help
Say Y here to enable support for user defined gesture functionality.
@@ -103,9 +103,9 @@
To compile this driver as a module, choose M here: the
module will be called synaptics_dsx_gesture.
-config TOUCHSCREEN_SYNAPTICS_DSX_VIDEO
- tristate "Synaptics DSX video module"
- depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
+config TOUCHSCREEN_SYNAPTICS_DSX_VIDEO_v26
+ tristate "Synaptics DSX v2.6 video module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26
help
Say Y here to enable support for video communication functionality.
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/Makefile b/drivers/input/touchscreen/synaptics_dsx_2.6/Makefile
index 515bc17..e5e7215 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/Makefile
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/Makefile
@@ -4,14 +4,14 @@
# Each configuration option enables a list of files.
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_I2C) += synaptics_dsx_i2c.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_SPI) += synaptics_dsx_spi.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_HID_I2C) += synaptics_dsx_rmi_hid_i2c.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE) += synaptics_dsx_core.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV) += synaptics_dsx_rmi_dev.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE) += synaptics_dsx_fw_update.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_TEST_REPORTING) += synaptics_dsx_test_reporting.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY) += synaptics_dsx_proximity.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_ACTIVE_PEN) += synaptics_dsx_active_pen.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_GESTURE) += synaptics_dsx_gesture.o
-obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_VIDEO) += synaptics_dsx_video.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_I2C_v26) += synaptics_dsx_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_SPI_v26) += synaptics_dsx_spi.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_HID_I2C_v26) += synaptics_dsx_rmi_hid_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_v26) += synaptics_dsx_core.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV_v26) += synaptics_dsx_rmi_dev.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_v26) += synaptics_dsx_fw_update.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_TEST_REPORTING_v26) += synaptics_dsx_test_reporting.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY_v26) += synaptics_dsx_proximity.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_ACTIVE_PEN_v26) += synaptics_dsx_active_pen.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_GESTURE_v26) += synaptics_dsx_gesture.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_VIDEO_v26) += synaptics_dsx_video.o
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_active_pen.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_active_pen.c
index 0e61708..db5324a 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_active_pen.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_active_pen.c
@@ -38,7 +38,7 @@
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define APEN_PHYS_NAME "synaptics_dsx/active_pen"
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c
index 1583299..b2f3bf5 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.c
@@ -40,7 +40,7 @@
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#ifdef KERNEL_ABOVE_2_6_38
#include <linux/input/mt.h>
@@ -67,11 +67,11 @@
#define F12_DATA_15_WORKAROUND
#define IGNORE_FN_INIT_FAILURE
-/*
+
#define FB_READY_RESET
#define FB_READY_WAIT_MS 100
#define FB_READY_TIMEOUT_S 30
-*/
+
#define RPT_TYPE (1 << 0)
#define RPT_X_LSB (1 << 1)
#define RPT_X_MSB (1 << 2)
@@ -596,25 +596,25 @@
static struct synaptics_dsx_button_map *vir_button_map;
static struct device_attribute attrs[] = {
- __ATTR(reset, S_IWUGO,
+ __ATTR(reset, 0220,
synaptics_rmi4_show_error,
synaptics_rmi4_f01_reset_store),
- __ATTR(productinfo, S_IRUGO,
+ __ATTR(productinfo, 0444,
synaptics_rmi4_f01_productinfo_show,
synaptics_rmi4_store_error),
- __ATTR(buildid, S_IRUGO,
+ __ATTR(buildid, 0444,
synaptics_rmi4_f01_buildid_show,
synaptics_rmi4_store_error),
- __ATTR(flashprog, S_IRUGO,
+ __ATTR(flashprog, 0444,
synaptics_rmi4_f01_flashprog_show,
synaptics_rmi4_store_error),
- __ATTR(0dbutton, (S_IRUGO | S_IWUGO),
+ __ATTR(0dbutton, 0664,
synaptics_rmi4_0dbutton_show,
synaptics_rmi4_0dbutton_store),
- __ATTR(suspend, S_IWUGO,
+ __ATTR(suspend, 0220,
synaptics_rmi4_show_error,
synaptics_rmi4_suspend_store),
- __ATTR(wake_gesture, (S_IRUGO | S_IWUGO),
+ __ATTR(wake_gesture, 0664,
synaptics_rmi4_wake_gesture_show,
synaptics_rmi4_wake_gesture_store),
};
@@ -1422,6 +1422,11 @@
rmi = &(rmi4_data->rmi4_mod_info);
+ if (rmi4_data->stay_awake) {
+ msleep(30);
+ return;
+ }
+
/*
* Get interrupt status information from F01 Data1 register to
* determine the source(s) that are flagging the interrupt.
@@ -4306,7 +4311,7 @@
{
int retval;
- retval = synaptics_rmi4_bus_init();
+ retval = synaptics_rmi4_bus_init_v26();
if (retval)
return retval;
@@ -4317,7 +4322,7 @@
{
platform_driver_unregister(&synaptics_rmi4_driver);
- synaptics_rmi4_bus_exit();
+ synaptics_rmi4_bus_exit_v26();
return;
}
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h
index 1b4079c..0de0e99 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_core.h
@@ -405,9 +405,9 @@
unsigned char intr_mask);
};
-int synaptics_rmi4_bus_init(void);
+int synaptics_rmi4_bus_init_v26(void);
-void synaptics_rmi4_bus_exit(void);
+void synaptics_rmi4_bus_exit_v26(void);
void synaptics_rmi4_new_function(struct synaptics_rmi4_exp_fn *exp_fn_module,
bool insert);
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c
index 5dd0ec4..9fb9beb 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_fw_update.c
@@ -39,7 +39,7 @@
#include <linux/input.h>
#include <linux/firmware.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define FW_IMAGE_NAME "synaptics/startup_fw_update.img"
@@ -661,49 +661,49 @@
};
static struct device_attribute attrs[] = {
- __ATTR(dorecovery, S_IWUGO,
+ __ATTR(dorecovery, 0220,
synaptics_rmi4_show_error,
fwu_sysfs_do_recovery_store),
- __ATTR(doreflash, S_IWUGO,
+ __ATTR(doreflash, 0220,
synaptics_rmi4_show_error,
fwu_sysfs_do_reflash_store),
- __ATTR(writeconfig, S_IWUGO,
+ __ATTR(writeconfig, 0220,
synaptics_rmi4_show_error,
fwu_sysfs_write_config_store),
- __ATTR(readconfig, S_IWUGO,
+ __ATTR(readconfig, 0220,
synaptics_rmi4_show_error,
fwu_sysfs_read_config_store),
- __ATTR(configarea, S_IWUGO,
+ __ATTR(configarea, 0220,
synaptics_rmi4_show_error,
fwu_sysfs_config_area_store),
- __ATTR(imagename, S_IWUGO,
+ __ATTR(imagename, 0220,
synaptics_rmi4_show_error,
fwu_sysfs_image_name_store),
- __ATTR(imagesize, S_IWUGO,
+ __ATTR(imagesize, 0220,
synaptics_rmi4_show_error,
fwu_sysfs_image_size_store),
- __ATTR(blocksize, S_IRUGO,
+ __ATTR(blocksize, 0444,
fwu_sysfs_block_size_show,
synaptics_rmi4_store_error),
- __ATTR(fwblockcount, S_IRUGO,
+ __ATTR(fwblockcount, 0444,
fwu_sysfs_firmware_block_count_show,
synaptics_rmi4_store_error),
- __ATTR(configblockcount, S_IRUGO,
+ __ATTR(configblockcount, 0444,
fwu_sysfs_configuration_block_count_show,
synaptics_rmi4_store_error),
- __ATTR(dispconfigblockcount, S_IRUGO,
+ __ATTR(dispconfigblockcount, 0444,
fwu_sysfs_disp_config_block_count_show,
synaptics_rmi4_store_error),
- __ATTR(permconfigblockcount, S_IRUGO,
+ __ATTR(permconfigblockcount, 0444,
fwu_sysfs_perm_config_block_count_show,
synaptics_rmi4_store_error),
- __ATTR(blconfigblockcount, S_IRUGO,
+ __ATTR(blconfigblockcount, 0444,
fwu_sysfs_bl_config_block_count_show,
synaptics_rmi4_store_error),
- __ATTR(guestcodeblockcount, S_IRUGO,
+ __ATTR(guestcodeblockcount, 0444,
fwu_sysfs_guest_code_block_count_show,
synaptics_rmi4_store_error),
- __ATTR(writeguestcode, S_IWUGO,
+ __ATTR(writeguestcode, 0220,
synaptics_rmi4_show_error,
fwu_sysfs_write_guest_code_store),
};
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_gesture.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_gesture.c
index 7630a60..0bd342c 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_gesture.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_gesture.c
@@ -38,7 +38,7 @@
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define GESTURE_PHYS_NAME "synaptics_dsx/gesture"
@@ -388,46 +388,46 @@
};
static struct device_attribute attrs[] = {
- __ATTR(engine_enable, S_IWUGO,
+ __ATTR(engine_enable, 0220,
synaptics_rmi4_show_error,
udg_sysfs_engine_enable_store),
- __ATTR(detection_enable, S_IWUGO,
+ __ATTR(detection_enable, 0220,
synaptics_rmi4_show_error,
udg_sysfs_detection_enable_store),
- __ATTR(detection_score, S_IRUGO,
+ __ATTR(detection_score, 0444,
udg_sysfs_detection_score_show,
synaptics_rmi4_store_error),
- __ATTR(detection_index, S_IRUGO,
+ __ATTR(detection_index, 0444,
udg_sysfs_detection_index_show,
synaptics_rmi4_store_error),
- __ATTR(registration_enable, S_IWUGO,
+ __ATTR(registration_enable, 0220,
synaptics_rmi4_show_error,
udg_sysfs_registration_enable_store),
- __ATTR(registration_begin, S_IWUGO,
+ __ATTR(registration_begin, 0220,
synaptics_rmi4_show_error,
udg_sysfs_registration_begin_store),
- __ATTR(registration_status, S_IRUGO,
+ __ATTR(registration_status, 0444,
udg_sysfs_registration_status_show,
synaptics_rmi4_store_error),
- __ATTR(template_size, S_IRUGO,
+ __ATTR(template_size, 0444,
udg_sysfs_template_size_show,
synaptics_rmi4_store_error),
- __ATTR(template_max_index, S_IRUGO,
+ __ATTR(template_max_index, 0444,
udg_sysfs_template_max_index_show,
synaptics_rmi4_store_error),
- __ATTR(template_detection, S_IRUGO,
+ __ATTR(template_detection, 0444,
udg_sysfs_template_detection_show,
synaptics_rmi4_store_error),
- __ATTR(template_index, S_IWUGO,
+ __ATTR(template_index, 0220,
synaptics_rmi4_show_error,
udg_sysfs_template_index_store),
- __ATTR(template_valid, (S_IRUGO | S_IWUGO),
+ __ATTR(template_valid, 0664,
udg_sysfs_template_valid_show,
udg_sysfs_template_valid_store),
- __ATTR(template_clear, S_IWUGO,
+ __ATTR(template_clear, 0220,
synaptics_rmi4_show_error,
udg_sysfs_template_clear_store),
- __ATTR(trace_size, S_IRUGO,
+ __ATTR(trace_size, 0444,
udg_sysfs_trace_size_show,
synaptics_rmi4_store_error),
};
@@ -435,7 +435,7 @@
static struct bin_attribute template_data = {
.attr = {
.name = "template_data",
- .mode = (S_IRUGO | S_IWUGO),
+ .mode = 0664,
},
.size = 0,
.read = udg_sysfs_template_data_show,
@@ -445,7 +445,7 @@
static struct bin_attribute trace_data = {
.attr = {
.name = "trace_data",
- .mode = S_IRUGO,
+ .mode = 0444,
},
.size = 0,
.read = udg_sysfs_trace_data_show,
@@ -453,22 +453,22 @@
};
static struct device_attribute params[] = {
- __ATTR(template_displacement, (S_IRUGO | S_IWUGO),
+ __ATTR(template_displacement, 0664,
udg_sysfs_template_displacement_show,
udg_sysfs_template_displacement_store),
- __ATTR(rotation_invariance, (S_IRUGO | S_IWUGO),
+ __ATTR(rotation_invariance, 0664,
udg_sysfs_rotation_invariance_show,
udg_sysfs_rotation_invariance_store),
- __ATTR(scale_invariance, (S_IRUGO | S_IWUGO),
+ __ATTR(scale_invariance, 0664,
udg_sysfs_scale_invariance_show,
udg_sysfs_scale_invariance_store),
- __ATTR(threshold_factor, (S_IRUGO | S_IWUGO),
+ __ATTR(threshold_factor, 0664,
udg_sysfs_threshold_factor_show,
udg_sysfs_threshold_factor_store),
- __ATTR(match_metric_threshold, (S_IRUGO | S_IWUGO),
+ __ATTR(match_metric_threshold, 0664,
udg_sysfs_match_metric_threshold_show,
udg_sysfs_match_metric_threshold_store),
- __ATTR(max_inter_stroke_time, (S_IRUGO | S_IWUGO),
+ __ATTR(max_inter_stroke_time, 0664,
udg_sysfs_max_inter_stroke_time_show,
udg_sysfs_max_inter_stroke_time_store),
};
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c
index 3be3bca..45951a4 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_i2c.c
@@ -40,7 +40,7 @@
#include <linux/types.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define SYN_I2C_RETRY_TIMES 10
@@ -48,9 +48,8 @@
/*
#define I2C_BURST_LIMIT 255
*/
-/*
+
#define XFER_MSGS_LIMIT 8
-*/
static unsigned char *wr_buf;
@@ -178,14 +177,9 @@
bdata->max_y_for_2d = -1;
}
- prop = of_find_property(np, "synaptics,swap-axes", NULL);
- bdata->swap_axes = prop > 0 ? true : false;
-
- prop = of_find_property(np, "synaptics,x-flip", NULL);
- bdata->x_flip = prop > 0 ? true : false;
-
- prop = of_find_property(np, "synaptics,y-flip", NULL);
- bdata->y_flip = prop > 0 ? true : false;
+ bdata->swap_axes = of_property_read_bool(np, "synaptics,swap-axes");
+ bdata->x_flip = of_property_read_bool(np, "synaptics,x-flip");
+ bdata->y_flip = of_property_read_bool(np, "synaptics,y-flip");
prop = of_find_property(np, "synaptics,ub-i2c-addr", NULL);
if (prop && prop->length) {
@@ -288,7 +282,7 @@
static int synaptics_rmi4_i2c_set_page(struct synaptics_rmi4_data *rmi4_data,
unsigned short addr)
{
- int retval;
+ int retval = 0;
unsigned char retry;
unsigned char buf[PAGE_SELECT_LEN];
unsigned char page;
@@ -306,7 +300,7 @@
if (page != rmi4_data->current_page) {
for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
- if (i2c_transfer(i2c->adapter, msg, 1) == 1) {
+ if (i2c_transfer(i2c->adapter, &msg[0], 1) == 1) {
rmi4_data->current_page = page;
retval = PAGE_SELECT_LEN;
break;
@@ -348,7 +342,7 @@
unsigned short remaining_length = length;
struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
struct i2c_adapter *adap = i2c->adapter;
- struct i2c_msg msg[rd_msgs + 1];
+ struct i2c_msg msg[XFER_MSGS_LIMIT + 1];
mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
@@ -384,14 +378,10 @@
remaining_msgs = rd_msgs + 1;
while (remaining_msgs) {
-#ifdef XFER_MSGS_LIMIT
if (remaining_msgs > XFER_MSGS_LIMIT)
xfer_msgs = XFER_MSGS_LIMIT;
else
xfer_msgs = remaining_msgs;
-#else
- xfer_msgs = remaining_msgs;
-#endif
for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
retval = i2c_transfer(adap, &msg[index], xfer_msgs);
if (retval == xfer_msgs)
@@ -469,7 +459,7 @@
}
for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
- if (i2c_transfer(i2c->adapter, msg, 1) == 1) {
+ if (i2c_transfer(i2c->adapter, &msg[0], 1) == 1) {
retval = length;
break;
}
@@ -625,13 +615,13 @@
.id_table = synaptics_rmi4_id_table,
};
-int synaptics_rmi4_bus_init(void)
+int synaptics_rmi4_bus_init_v26(void)
{
return i2c_add_driver(&synaptics_rmi4_i2c_driver);
}
-EXPORT_SYMBOL(synaptics_rmi4_bus_init);
+EXPORT_SYMBOL(synaptics_rmi4_bus_init_v26);
-void synaptics_rmi4_bus_exit(void)
+void synaptics_rmi4_bus_exit_v26(void)
{
kfree(wr_buf);
@@ -639,7 +629,7 @@
return;
}
-EXPORT_SYMBOL(synaptics_rmi4_bus_exit);
+EXPORT_SYMBOL(synaptics_rmi4_bus_exit_v26);
MODULE_AUTHOR("Synaptics, Inc.");
MODULE_DESCRIPTION("Synaptics DSX I2C Bus Support Module");
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_proximity.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_proximity.c
index 2507c77..ce8979c 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_proximity.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_proximity.c
@@ -38,7 +38,7 @@
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define PROX_PHYS_NAME "synaptics_dsx/proximity"
@@ -54,7 +54,7 @@
struct device_attribute *attr, const char *buf, size_t count);
static struct device_attribute attrs[] = {
- __ATTR(hover_finger_en, (S_IRUGO | S_IWUGO),
+ __ATTR(hover_finger_en, 0664,
synaptics_rmi4_hover_finger_en_show,
synaptics_rmi4_hover_finger_en_store),
};
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_dev.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_dev.c
index 9d19bda..3f57d13 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_dev.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_dev.c
@@ -43,7 +43,7 @@
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define CHAR_DEVICE_NAME "rmi"
@@ -118,7 +118,7 @@
static struct bin_attribute attr_data = {
.attr = {
.name = "data",
- .mode = (S_IRUGO | S_IWUGO),
+ .mode = 0664,
},
.size = 0,
.read = rmidev_sysfs_data_show,
@@ -126,25 +126,25 @@
};
static struct device_attribute attrs[] = {
- __ATTR(open, S_IWUGO,
+ __ATTR(open, 0220,
synaptics_rmi4_show_error,
rmidev_sysfs_open_store),
- __ATTR(release, S_IWUGO,
+ __ATTR(release, 0220,
synaptics_rmi4_show_error,
rmidev_sysfs_release_store),
- __ATTR(attn_state, S_IRUGO,
+ __ATTR(attn_state, 0444,
rmidev_sysfs_attn_state_show,
synaptics_rmi4_store_error),
- __ATTR(pid, S_IRUGO | S_IWUGO,
+ __ATTR(pid, 0664,
rmidev_sysfs_pid_show,
rmidev_sysfs_pid_store),
- __ATTR(term, S_IWUGO,
+ __ATTR(term, 0220,
synaptics_rmi4_show_error,
rmidev_sysfs_term_store),
- __ATTR(intr_mask, S_IRUGO | S_IWUGO,
+ __ATTR(intr_mask, 0444,
rmidev_sysfs_intr_mask_show,
rmidev_sysfs_intr_mask_store),
- __ATTR(concurrent, S_IRUGO | S_IWUGO,
+ __ATTR(concurrent, 0444,
rmidev_sysfs_concurrent_show,
rmidev_sysfs_concurrent_store),
};
@@ -155,7 +155,7 @@
static struct rmidev_handle *rmidev;
-DECLARE_COMPLETION(rmidev_remove_complete);
+DECLARE_COMPLETION(rmidev_remove_complete_v26);
static irqreturn_t rmidev_sysfs_irq(int irq, void *data)
{
@@ -1016,7 +1016,7 @@
rmidev = NULL;
exit:
- complete(&rmidev_remove_complete);
+ complete(&rmidev_remove_complete_v26);
return;
}
@@ -1045,7 +1045,7 @@
{
synaptics_rmi4_new_function(&rmidev_module, false);
- wait_for_completion(&rmidev_remove_complete);
+ wait_for_completion(&rmidev_remove_complete_v26);
return;
}
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_hid_i2c.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_hid_i2c.c
index df6e357..7e02487 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_hid_i2c.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_rmi_hid_i2c.c
@@ -42,7 +42,7 @@
#include <linux/types.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define SYN_I2C_RETRY_TIMES 10
@@ -987,19 +987,19 @@
.id_table = synaptics_rmi4_id_table,
};
-int synaptics_rmi4_bus_init(void)
+int synaptics_rmi4_bus_init_v26(void)
{
return i2c_add_driver(&synaptics_rmi4_i2c_driver);
}
-EXPORT_SYMBOL(synaptics_rmi4_bus_init);
+EXPORT_SYMBOL(synaptics_rmi4_bus_init_v26);
-void synaptics_rmi4_bus_exit(void)
+void synaptics_rmi4_bus_exit_v26(void)
{
i2c_del_driver(&synaptics_rmi4_i2c_driver);
return;
}
-EXPORT_SYMBOL(synaptics_rmi4_bus_exit);
+EXPORT_SYMBOL(synaptics_rmi4_bus_exit_v26);
MODULE_AUTHOR("Synaptics, Inc.");
MODULE_DESCRIPTION("Synaptics DSX I2C Bus Support Module");
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_spi.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_spi.c
index d5d9c3c..382a3dd 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_spi.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_spi.c
@@ -40,7 +40,7 @@
#include <linux/types.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define SPI_READ 0x80
@@ -615,19 +615,19 @@
};
-int synaptics_rmi4_bus_init(void)
+int synaptics_rmi4_bus_init_v26(void)
{
return spi_register_driver(&synaptics_rmi4_spi_driver);
}
-EXPORT_SYMBOL(synaptics_rmi4_bus_init);
+EXPORT_SYMBOL(synaptics_rmi4_bus_init_v26);
-void synaptics_rmi4_bus_exit(void)
+void synaptics_rmi4_bus_exit_v26(void)
{
spi_unregister_driver(&synaptics_rmi4_spi_driver);
return;
}
-EXPORT_SYMBOL(synaptics_rmi4_bus_exit);
+EXPORT_SYMBOL(synaptics_rmi4_bus_exit_v26);
MODULE_AUTHOR("Synaptics, Inc.");
MODULE_DESCRIPTION("Synaptics DSX SPI Bus Support Module");
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_test_reporting.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_test_reporting.c
index 6ed7bd6..1fdd89f 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_test_reporting.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_test_reporting.c
@@ -40,7 +40,7 @@
#include <linux/ctype.h>
#include <linux/hrtimer.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define SYSFS_FOLDER_NAME "f54"
@@ -201,7 +201,7 @@
char *buf);\
\
static struct device_attribute dev_attr_##propname =\
- __ATTR(propname, S_IRUGO,\
+ __ATTR(propname, 0444,\
concat(test_sysfs, _##propname##_show),\
synaptics_rmi4_store_error);
@@ -212,7 +212,7 @@
const char *buf, size_t count);\
\
static struct device_attribute dev_attr_##propname =\
- __ATTR(propname, S_IWUGO,\
+ __ATTR(propname, 0220,\
synaptics_rmi4_show_error,\
concat(test_sysfs, _##propname##_store));
@@ -228,7 +228,7 @@
const char *buf, size_t count);\
\
static struct device_attribute dev_attr_##propname =\
- __ATTR(propname, (S_IRUGO | S_IWUGO),\
+ __ATTR(propname, 0664,\
concat(test_sysfs, _##propname##_show),\
concat(test_sysfs, _##propname##_store));
diff --git a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_video.c b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_video.c
index 493410e..312d203 100644
--- a/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_video.c
+++ b/drivers/input/touchscreen/synaptics_dsx_2.6/synaptics_dsx_video.c
@@ -37,7 +37,7 @@
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/platform_device.h>
-#include <linux/input/synaptics_dsx.h>
+#include <linux/input/synaptics_dsx_v2_6.h>
#include "synaptics_dsx_core.h"
#define SYSFS_FOLDER_NAME "video"
@@ -112,10 +112,10 @@
#endif
static struct device_attribute attrs[] = {
- __ATTR(dcs_write, S_IWUGO,
+ __ATTR(dcs_write, 0220,
synaptics_rmi4_show_error,
video_sysfs_dcs_write_store),
- __ATTR(param, S_IWUGO,
+ __ATTR(param, 0220,
synaptics_rmi4_show_error,
video_sysfs_param_store),
};
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index d3d975a..7f294f7 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -1547,13 +1547,15 @@
domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
domain->geometry.aperture_end = (1UL << ias) - 1;
domain->geometry.force_aperture = true;
- smmu_domain->pgtbl_ops = pgtbl_ops;
ret = finalise_stage_fn(smmu_domain, &pgtbl_cfg);
- if (ret < 0)
+ if (ret < 0) {
free_io_pgtable_ops(pgtbl_ops);
+ return ret;
+ }
- return ret;
+ smmu_domain->pgtbl_ops = pgtbl_ops;
+ return 0;
}
static __le64 *arm_smmu_get_step_for_sid(struct arm_smmu_device *smmu, u32 sid)
@@ -1580,7 +1582,7 @@
static int arm_smmu_install_ste_for_dev(struct iommu_fwspec *fwspec)
{
- int i;
+ int i, j;
struct arm_smmu_master_data *master = fwspec->iommu_priv;
struct arm_smmu_device *smmu = master->smmu;
@@ -1588,6 +1590,13 @@
u32 sid = fwspec->ids[i];
__le64 *step = arm_smmu_get_step_for_sid(smmu, sid);
+ /* Bridged PCI devices may end up with duplicated IDs */
+ for (j = 0; j < i; j++)
+ if (fwspec->ids[j] == sid)
+ break;
+ if (j < i)
+ continue;
+
arm_smmu_write_strtab_ent(smmu, sid, step, &master->ste);
}
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index d8d9011..85df514 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -56,6 +56,7 @@
#include <linux/ktime.h>
#include <trace/events/iommu.h>
#include <linux/notifier.h>
+#include <dt-bindings/arm/arm-smmu.h>
#include <linux/amba/bus.h>
#include <soc/qcom/msm_tz_smmu.h>
@@ -431,16 +432,6 @@
#define ARM_SMMU_FEAT_FMT_AARCH32_S (1 << 11)
u32 features;
-#define ARM_SMMU_OPT_SECURE_CFG_ACCESS (1 << 0)
-#define ARM_SMMU_OPT_FATAL_ASF (1 << 1)
-#define ARM_SMMU_OPT_SKIP_INIT (1 << 2)
-#define ARM_SMMU_OPT_DYNAMIC (1 << 3)
-#define ARM_SMMU_OPT_3LVL_TABLES (1 << 4)
-#define ARM_SMMU_OPT_NO_ASID_RETENTION (1 << 5)
-#define ARM_SMMU_OPT_DISABLE_ATOS (1 << 6)
-#define ARM_SMMU_OPT_MMU500_ERRATA1 (1 << 7)
-#define ARM_SMMU_OPT_STATIC_CB (1 << 8)
-#define ARM_SMMU_OPT_HALT (1 << 9)
u32 options;
enum arm_smmu_arch_version version;
enum arm_smmu_implementation model;
@@ -448,6 +439,7 @@
u32 num_context_banks;
u32 num_s2_context_banks;
DECLARE_BITMAP(context_map, ARM_SMMU_MAX_CBS);
+ DECLARE_BITMAP(secure_context_map, ARM_SMMU_MAX_CBS);
atomic_t irptndx;
u32 num_mapping_groups;
@@ -627,7 +619,7 @@
return !!(smmu_domain->attributes & (1 << DOMAIN_ATTR_DYNAMIC));
}
-static int arm_smmu_restore_sec_cfg(struct arm_smmu_device *smmu)
+static int arm_smmu_restore_sec_cfg(struct arm_smmu_device *smmu, u32 cb)
{
int ret;
int scm_ret = 0;
@@ -635,7 +627,7 @@
if (!arm_smmu_is_static_cb(smmu))
return 0;
- ret = scm_restore_sec_cfg(smmu->sec_id, 0x0, &scm_ret);
+ ret = scm_restore_sec_cfg(smmu->sec_id, cb, &scm_ret);
if (ret || scm_ret) {
pr_err("scm call IOMMU_SECURE_CFG failed\n");
return -EINVAL;
@@ -1555,6 +1547,19 @@
return IRQ_HANDLED;
}
+static bool arm_smmu_master_attached(struct arm_smmu_device *smmu,
+ struct iommu_fwspec *fwspec)
+{
+ int i, idx;
+
+ for_each_cfg_sme(fwspec, i, idx) {
+ if (smmu->s2crs[idx].attach_count)
+ return true;
+ }
+
+ return false;
+}
+
static int arm_smmu_set_pt_format(struct arm_smmu_domain *smmu_domain,
struct io_pgtable_cfg *pgtbl_cfg)
{
@@ -1965,6 +1970,10 @@
/* Publish page table ops for map/unmap */
smmu_domain->pgtbl_ops = pgtbl_ops;
+ if (arm_smmu_is_slave_side_secure(smmu_domain) &&
+ !arm_smmu_master_attached(smmu, dev->iommu_fwspec))
+ arm_smmu_restore_sec_cfg(smmu, cfg->cbndx);
+
return 0;
out_clear_smmu:
@@ -2033,6 +2042,11 @@
arm_smmu_unassign_table(smmu_domain);
arm_smmu_secure_domain_unlock(smmu_domain);
__arm_smmu_free_bitmap(smmu->context_map, cfg->cbndx);
+ /* As the nonsecure context bank index is any way set to zero,
+ * so, directly clearing up the secure cb bitmap.
+ */
+ if (arm_smmu_is_slave_side_secure(smmu_domain))
+ __arm_smmu_free_bitmap(smmu->secure_context_map, cfg->cbndx);
arm_smmu_power_off(smmu->pwr);
arm_smmu_domain_reinit(smmu_domain);
@@ -2758,13 +2772,24 @@
bool arm_smmu_skip_write(void __iomem *addr)
{
struct arm_smmu_device *smmu;
+ int cb;
smmu = arm_smmu_get_by_addr(addr);
- if (smmu &&
- ((unsigned long)addr & (smmu->size - 1)) >= (smmu->size >> 1))
- return false;
- else
+
+ /* Skip write if smmu not available by now */
+ if (!smmu)
return true;
+
+ /* Do not write to global space */
+ if (((unsigned long)addr & (smmu->size - 1)) < (smmu->size >> 1))
+ return true;
+
+ /* Finally skip writing to secure CB */
+ cb = ((unsigned long)addr & ((smmu->size >> 1) - 1)) >> PAGE_SHIFT;
+ if (test_bit(cb, smmu->secure_context_map))
+ return true;
+
+ return false;
}
#endif
@@ -3698,9 +3723,13 @@
cb = smmu->s2crs[idx].cbndx;
}
- if (cb >= 0 && arm_smmu_is_static_cb(smmu))
+ if (cb >= 0 && arm_smmu_is_static_cb(smmu)) {
smmu_domain->slave_side_secure = true;
+ if (arm_smmu_is_slave_side_secure(smmu_domain))
+ bitmap_set(smmu->secure_context_map, cb, 1);
+ }
+
if (cb < 0 && !arm_smmu_is_static_cb(smmu)) {
mutex_unlock(&smmu->stream_map_mutex);
return __arm_smmu_alloc_bitmap(smmu->context_map,
@@ -3875,7 +3904,7 @@
if (event == REGULATOR_EVENT_PRE_DISABLE)
qsmmuv2_halt(smmu);
else if (event == REGULATOR_EVENT_ENABLE) {
- if (arm_smmu_restore_sec_cfg(smmu))
+ if (arm_smmu_restore_sec_cfg(smmu, 0))
goto power_off;
qsmmuv2_resume(smmu);
}
@@ -4028,7 +4057,7 @@
bool cttw_dt, cttw_reg;
int i;
- if (arm_smmu_restore_sec_cfg(smmu))
+ if (arm_smmu_restore_sec_cfg(smmu, 0))
return -ENODEV;
dev_dbg(smmu->dev, "probing hardware configuration...\n");
@@ -4514,7 +4543,8 @@
if (arm_smmu_power_on(smmu->pwr))
return -EINVAL;
- if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS))
+ if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS) ||
+ !bitmap_empty(smmu->secure_context_map, ARM_SMMU_MAX_CBS))
dev_err(&pdev->dev, "removing device with active domains!\n");
idr_destroy(&smmu->asid_idr);
@@ -5282,6 +5312,9 @@
data->version = readl_relaxed(data->tcu_base + TCU_HW_VERSION_HLOS1);
smmu->archdata = data;
+ if (arm_smmu_is_static_cb(smmu))
+ return 0;
+
ret = qsmmuv500_parse_errata1(smmu);
if (ret)
return ret;
diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c
index 22a708e..25b85ab 100644
--- a/drivers/iommu/iommu-debug.c
+++ b/drivers/iommu/iommu-debug.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2018, 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
@@ -28,7 +28,7 @@
#include <asm/cacheflush.h>
#include <asm/dma-iommu.h>
-#if defined(CONFIG_IOMMU_DEBUG_TRACKING) || defined(CONFIG_IOMMU_TESTS)
+#if defined(CONFIG_IOMMU_TESTS)
static const char *iommu_debug_attr_to_string(enum iommu_attr attr)
{
@@ -170,6 +170,8 @@
u64 phys;
size_t len;
struct list_head list;
+ struct mutex clk_lock;
+ unsigned int clk_count;
};
static int iommu_debug_build_phoney_sg_table(struct device *dev,
@@ -1195,6 +1197,7 @@
return -ENOMEM;
}
+ val = VMID_CP_CAMERA;
if (is_secure && iommu_domain_set_attr(ddev->domain,
DOMAIN_ATTR_SECURE_VMID,
&val)) {
@@ -1485,6 +1488,10 @@
ssize_t retval;
size_t buflen;
+ if (kptr_restrict != 0) {
+ pr_err("kptr_restrict needs to be disabled.\n");
+ return -EPERM;
+ }
if (!dev->archdata.mapping) {
pr_err("No mapping. Did you already attach?\n");
return -EINVAL;
@@ -1552,6 +1559,10 @@
ssize_t retval;
size_t buflen;
+ if (kptr_restrict != 0) {
+ pr_err("kptr_restrict needs to be disabled.\n");
+ return -EPERM;
+ }
if (!ddev->domain) {
pr_err("No domain. Did you already attach?\n");
return -EINVAL;
@@ -1600,6 +1611,10 @@
ssize_t retval;
size_t buflen;
+ if (kptr_restrict != 0) {
+ pr_err("kptr_restrict needs to be disabled.\n");
+ return -EPERM;
+ }
if (!dev->archdata.mapping) {
pr_err("No mapping. Did you already attach?\n");
return -EINVAL;
@@ -2046,20 +2061,34 @@
return -EFAULT;
}
+ mutex_lock(&ddev->clk_lock);
switch (buf) {
case '0':
+ if (ddev->clk_count == 0) {
+ dev_err(dev, "Config clocks already disabled\n");
+ break;
+ }
+
+ if (--ddev->clk_count > 0)
+ break;
+
dev_err(dev, "Disabling config clocks\n");
iommu_disable_config_clocks(ddev->domain);
break;
case '1':
+ if (ddev->clk_count++ > 0)
+ break;
+
dev_err(dev, "Enabling config clocks\n");
if (iommu_enable_config_clocks(ddev->domain))
dev_err(dev, "Failed!\n");
break;
default:
dev_err(dev, "Invalid value. Should be 0 or 1.\n");
+ mutex_unlock(&ddev->clk_lock);
return -EINVAL;
}
+ mutex_unlock(&ddev->clk_lock);
return count;
}
@@ -2109,6 +2138,9 @@
if (!of_find_property(dev->of_node, "iommus", NULL))
return 0;
+ if (!of_device_is_compatible(dev->of_node, "iommu-debug-test"))
+ return 0;
+
/* Hold a reference count */
if (!iommu_group_get(dev))
return 0;
@@ -2116,6 +2148,7 @@
ddev = kzalloc(sizeof(*ddev), GFP_KERNEL);
if (!ddev)
return -ENODEV;
+ mutex_init(&ddev->clk_lock);
ddev->dev = dev;
dir = debugfs_create_dir(dev_name(dev), debugfs_tests_dir);
if (!dir) {
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 26e03ba..787bda3 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -625,6 +625,15 @@
To compile this driver as a module, choose 'm' here: the module
will be called leds-powernv.
+config LEDS_QTI_TRI_LED
+ tristate "LED support for Qualcomm Technologies, Inc. TRI_LED"
+ depends on LEDS_CLASS && MFD_SPMI_PMIC && PWM && OF
+ help
+ This driver supports the TRI_LED module found in Qualcomm
+ Technologies, Inc. PMIC chips. TRI_LED supports 3 LED drivers
+ at max and each is controlled by a PWM channel used for dimming
+ or blinking.
+
config LEDS_SYSCON
bool "LED support for LEDs on system controllers"
depends on LEDS_CLASS=y
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 5514391..e9eaa50 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -61,6 +61,7 @@
obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
+obj-$(CONFIG_LEDS_QTI_TRI_LED) += leds-qti-tri-led.o
obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o
obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o
obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o
@@ -72,8 +73,8 @@
obj-$(CONFIG_LEDS_PM8058) += leds-pm8058.o
obj-$(CONFIG_LEDS_MLXCPLD) += leds-mlxcpld.o
obj-$(CONFIG_LEDS_QPNP) += leds-qpnp.o
-obj-$(CONFIG_LEDS_QPNP_FLASH) += leds-qpnp-flash.o
-obj-$(CONFIG_LEDS_QPNP_FLASH_V2) += leds-qpnp-flash-v2.o
+obj-$(CONFIG_LEDS_QPNP_FLASH) += leds-qpnp-flash.o leds-qpnp-flash-common.o
+obj-$(CONFIG_LEDS_QPNP_FLASH_V2) += leds-qpnp-flash-v2.o leds-qpnp-flash-common.o
obj-$(CONFIG_LEDS_QPNP_WLED) += leds-qpnp-wled.o
obj-$(CONFIG_LEDS_QPNP_HAPTICS) += leds-qpnp-haptics.o
obj-$(CONFIG_LEDS_QPNP_VIBRATOR_LDO) += leds-qpnp-vibrator-ldo.o
diff --git a/drivers/leds/leds-qpnp-flash-common.c b/drivers/leds/leds-qpnp-flash-common.c
new file mode 100644
index 0000000..5aed910
--- /dev/null
+++ b/drivers/leds/leds-qpnp-flash-common.c
@@ -0,0 +1,16 @@
+/* Copyright (c) 2018, 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.
+ */
+
+#include <linux/leds-qpnp-flash.h>
+
+int (*qpnp_flash_led_prepare)(struct led_trigger *trig, int options,
+ int *max_current);
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index 1a2aea9..f3f9a1a 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -99,11 +99,8 @@
#define VPH_DROOP_DEBOUNCE_US_TO_VAL(val_us) (val_us / 8)
#define VPH_DROOP_HYST_MV_TO_VAL(val_mv) (val_mv / 25)
-#define VPH_DROOP_THRESH_MV_TO_VAL(val_mv) ((val_mv / 100) - 25)
#define VPH_DROOP_THRESH_VAL_TO_UV(val) ((val + 25) * 100000)
#define MITIGATION_THRSH_MA_TO_VAL(val_ma) (val_ma / 100)
-#define CURRENT_MA_TO_REG_VAL(curr_ma, ires_ua) ((curr_ma * 1000) / ires_ua - 1)
-#define SAFETY_TMR_TO_REG_VAL(duration_ms) ((duration_ms / 10) - 1)
#define THERMAL_HYST_TEMP_TO_VAL(val, divisor) (val / divisor)
#define FLASH_LED_ISC_WARMUP_DELAY_SHIFT 6
@@ -317,6 +314,14 @@
FLASH_LED_IRES7P5_MAX_CURR_MA, FLASH_LED_IRES5P0_MAX_CURR_MA
};
+static inline int get_current_reg_code(int target_curr_ma, int ires_ua)
+{
+ if (!ires_ua || !target_curr_ma || (target_curr_ma < (ires_ua / 1000)))
+ return 0;
+
+ return DIV_ROUND_UP(target_curr_ma * 1000, ires_ua) - 1;
+}
+
static int qpnp_flash_led_read(struct qpnp_flash_led *led, u16 addr, u8 *data)
{
int rc;
@@ -542,7 +547,7 @@
return rc;
if (led->pdata->led1n2_iclamp_low_ma) {
- val = CURRENT_MA_TO_REG_VAL(led->pdata->led1n2_iclamp_low_ma,
+ val = get_current_reg_code(led->pdata->led1n2_iclamp_low_ma,
led->fnode[LED1].ires_ua);
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_LED1N2_ICLAMP_LOW(led->base),
@@ -552,7 +557,7 @@
}
if (led->pdata->led1n2_iclamp_mid_ma) {
- val = CURRENT_MA_TO_REG_VAL(led->pdata->led1n2_iclamp_mid_ma,
+ val = get_current_reg_code(led->pdata->led1n2_iclamp_mid_ma,
led->fnode[LED1].ires_ua);
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_LED1N2_ICLAMP_MID(led->base),
@@ -562,7 +567,7 @@
}
if (led->pdata->led3_iclamp_low_ma) {
- val = CURRENT_MA_TO_REG_VAL(led->pdata->led3_iclamp_low_ma,
+ val = get_current_reg_code(led->pdata->led3_iclamp_low_ma,
led->fnode[LED3].ires_ua);
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_LED3_ICLAMP_LOW(led->base),
@@ -572,7 +577,7 @@
}
if (led->pdata->led3_iclamp_mid_ma) {
- val = CURRENT_MA_TO_REG_VAL(led->pdata->led3_iclamp_mid_ma,
+ val = get_current_reg_code(led->pdata->led3_iclamp_mid_ma,
led->fnode[LED3].ires_ua);
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_LED3_ICLAMP_MID(led->base),
@@ -992,7 +997,7 @@
}
fnode->current_ma = prgm_current_ma;
fnode->cdev.brightness = prgm_current_ma;
- fnode->current_reg_val = CURRENT_MA_TO_REG_VAL(prgm_current_ma,
+ fnode->current_reg_val = get_current_reg_code(prgm_current_ma,
fnode->ires_ua);
fnode->led_on = prgm_current_ma != 0;
@@ -1103,10 +1108,11 @@
return rc;
}
- /* Iterate over all leds for this switch node */
+ /* Iterate over all active leds for this switch node */
val = 0;
for (i = 0; i < led->num_fnodes; i++)
- if (snode->led_mask & BIT(led->fnode[i].id))
+ if (led->fnode[i].led_on &&
+ snode->led_mask & BIT(led->fnode[i].id))
val |= led->fnode[i].ires_idx << (led->fnode[i].id * 2);
rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_IRES(led->base),
@@ -1210,7 +1216,7 @@
return 0;
}
-int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
+static int qpnp_flash_led_prepare_v2(struct led_trigger *trig, int options,
int *max_current)
{
struct led_classdev *led_cdev;
@@ -1430,6 +1436,22 @@
return atomic_notifier_chain_unregister(&irq_notifier_list, nb);
}
+static inline u8 get_safety_timer_code(u32 duration_ms)
+{
+ if (!duration_ms)
+ return 0;
+
+ return (duration_ms / 10) - 1;
+}
+
+static inline u8 get_vph_droop_thresh_code(u32 val_mv)
+{
+ if (!val_mv)
+ return 0;
+
+ return (val_mv / 100) - 25;
+}
+
static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
struct flash_node_data *fnode, struct device_node *node)
{
@@ -1521,8 +1543,9 @@
fnode->duration = FLASH_LED_SAFETY_TMR_DISABLED;
rc = of_property_read_u32(node, "qcom,duration-ms", &val);
if (!rc) {
- fnode->duration = (u8)(SAFETY_TMR_TO_REG_VAL(val) |
- FLASH_LED_SAFETY_TMR_ENABLE);
+ fnode->duration = get_safety_timer_code(val);
+ if (fnode->duration)
+ fnode->duration |= FLASH_LED_SAFETY_TMR_ENABLE;
} else if (rc == -EINVAL) {
if (fnode->type == FLASH_LED_TYPE_FLASH) {
pr_err("Timer duration is required for flash LED\n");
@@ -1968,7 +1991,7 @@
rc = of_property_read_u32(node, "qcom,vph-droop-threshold-mv", &val);
if (!rc) {
led->pdata->vph_droop_threshold =
- VPH_DROOP_THRESH_MV_TO_VAL(val);
+ get_vph_droop_thresh_code(val);
} else if (rc != -EINVAL) {
pr_err("Unable to read VPH droop threshold, rc=%d\n", rc);
return rc;
@@ -2194,6 +2217,7 @@
if (!led->pdata)
return -ENOMEM;
+ qpnp_flash_led_prepare = qpnp_flash_led_prepare_v2;
rc = qpnp_flash_led_parse_common_dt(led, node);
if (rc < 0) {
pr_err("Failed to parse common flash LED device tree\n");
diff --git a/drivers/leds/leds-qpnp-flash.c b/drivers/leds/leds-qpnp-flash.c
index 3b07af8..ce2b055 100644
--- a/drivers/leds/leds-qpnp-flash.c
+++ b/drivers/leds/leds-qpnp-flash.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2018, 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
@@ -1207,7 +1207,7 @@
return rc;
}
-int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
+static int qpnp_flash_led_prepare_v1(struct led_trigger *trig, int options,
int *max_current)
{
struct led_classdev *led_cdev = trigger_to_lcdev(trig);
@@ -2468,6 +2468,7 @@
led->pdev = pdev;
led->current_addr = FLASH_LED0_CURRENT(led->base);
led->current2_addr = FLASH_LED1_CURRENT(led->base);
+ qpnp_flash_led_prepare = qpnp_flash_led_prepare_v1;
led->pdata = devm_kzalloc(&pdev->dev, sizeof(*led->pdata), GFP_KERNEL);
if (!led->pdata)
diff --git a/drivers/leds/leds-qti-tri-led.c b/drivers/leds/leds-qti-tri-led.c
new file mode 100644
index 0000000..ab5e876
--- /dev/null
+++ b/drivers/leds/leds-qti-tri-led.c
@@ -0,0 +1,512 @@
+/* Copyright (c) 2018, 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#define TRILED_REG_TYPE 0x04
+#define TRILED_REG_SUBTYPE 0x05
+#define TRILED_REG_EN_CTL 0x46
+
+/* TRILED_REG_EN_CTL */
+#define TRILED_EN_CTL_MASK GENMASK(7, 5)
+#define TRILED_EN_CTL_MAX_BIT 7
+
+#define TRILED_TYPE 0x19
+#define TRILED_SUBTYPE_LED3H0L12 0x02
+#define TRILED_SUBTYPE_LED2H0L12 0x03
+#define TRILED_SUBTYPE_LED1H2L12 0x04
+
+#define TRILED_NUM_MAX 3
+
+#define PWM_PERIOD_DEFAULT_NS 1000000
+#define LED_BLINK_ON_MS 125
+#define LED_BLINK_OFF_MS 875
+
+struct pwm_setting {
+ u32 pre_period_ns;
+ u32 period_ns;
+ u32 duty_ns;
+};
+
+struct led_setting {
+ u32 on_ms;
+ u32 off_ms;
+ enum led_brightness brightness;
+ bool blink;
+};
+
+struct qpnp_led_dev {
+ struct led_classdev cdev;
+ struct pwm_device *pwm_dev;
+ struct pwm_setting pwm_setting;
+ struct led_setting led_setting;
+ struct qpnp_tri_led_chip *chip;
+ struct mutex lock;
+ const char *label;
+ const char *default_trigger;
+ u8 id;
+ bool blinking;
+};
+
+struct qpnp_tri_led_chip {
+ struct device *dev;
+ struct regmap *regmap;
+ struct qpnp_led_dev *leds;
+ struct mutex bus_lock;
+ int num_leds;
+ u16 reg_base;
+ u8 subtype;
+};
+
+static int qpnp_tri_led_read(struct qpnp_tri_led_chip *chip, u16 addr, u8 *val)
+{
+ int rc;
+ unsigned int tmp;
+
+ mutex_lock(&chip->bus_lock);
+ rc = regmap_read(chip->regmap, chip->reg_base + addr, &tmp);
+ if (rc < 0)
+ dev_err(chip->dev, "Read addr 0x%x failed, rc=%d\n", addr, rc);
+ else
+ *val = (u8)tmp;
+ mutex_unlock(&chip->bus_lock);
+
+ return rc;
+}
+
+static int qpnp_tri_led_masked_write(struct qpnp_tri_led_chip *chip,
+ u16 addr, u8 mask, u8 val)
+{
+ int rc;
+
+ mutex_lock(&chip->bus_lock);
+ rc = regmap_update_bits(chip->regmap, chip->reg_base + addr, mask, val);
+ if (rc < 0)
+ dev_err(chip->dev, "Update addr 0x%x to val 0x%x with mask 0x%x failed, rc=%d\n",
+ addr, val, mask, rc);
+ mutex_unlock(&chip->bus_lock);
+
+ return rc;
+}
+
+static int __tri_led_config_pwm(struct qpnp_led_dev *led,
+ struct pwm_setting *pwm)
+{
+ struct pwm_state pstate;
+ int rc;
+
+ pwm_get_state(led->pwm_dev, &pstate);
+ pstate.enabled = !!(pwm->duty_ns != 0);
+ pstate.period = pwm->period_ns;
+ pstate.duty_cycle = pwm->duty_ns;
+ rc = pwm_apply_state(led->pwm_dev, &pstate);
+
+ if (rc < 0)
+ dev_err(led->chip->dev, "Apply PWM state for %s led failed, rc=%d\n",
+ led->cdev.name, rc);
+
+ return rc;
+}
+
+static int __tri_led_set(struct qpnp_led_dev *led)
+{
+ int rc = 0;
+ u8 val = 0, mask = 0;
+
+ rc = __tri_led_config_pwm(led, &led->pwm_setting);
+ if (rc < 0) {
+ dev_err(led->chip->dev, "Configure PWM for %s led failed, rc=%d\n",
+ led->cdev.name, rc);
+ return rc;
+ }
+
+ mask |= 1 << (TRILED_EN_CTL_MAX_BIT - led->id);
+
+ if (led->pwm_setting.duty_ns == 0)
+ val = 0;
+ else
+ val = mask;
+
+ rc = qpnp_tri_led_masked_write(led->chip, TRILED_REG_EN_CTL,
+ mask, val);
+ if (rc < 0)
+ dev_err(led->chip->dev, "Update addr 0x%x failed, rc=%d\n",
+ TRILED_REG_EN_CTL, rc);
+
+ return rc;
+}
+
+static int qpnp_tri_led_set(struct qpnp_led_dev *led)
+{
+ u32 on_ms, off_ms, period_ns, duty_ns;
+ enum led_brightness brightness = led->led_setting.brightness;
+ int rc = 0;
+
+ if (led->led_setting.blink) {
+ on_ms = led->led_setting.on_ms;
+ off_ms = led->led_setting.off_ms;
+ if (on_ms > INT_MAX / NSEC_PER_MSEC)
+ duty_ns = INT_MAX - 1;
+ else
+ duty_ns = on_ms * NSEC_PER_MSEC;
+
+ if (on_ms + off_ms > INT_MAX / NSEC_PER_MSEC) {
+ period_ns = INT_MAX;
+ duty_ns = (period_ns / (on_ms + off_ms)) * on_ms;
+ } else {
+ period_ns = (on_ms + off_ms) * NSEC_PER_MSEC;
+ }
+
+ if (period_ns < duty_ns && duty_ns != 0)
+ period_ns = duty_ns + 1;
+ } else {
+ /* Use initial period if no blinking is required */
+ period_ns = led->pwm_setting.pre_period_ns;
+
+ if (period_ns > INT_MAX / brightness)
+ duty_ns = (period_ns / LED_FULL) * brightness;
+ else
+ duty_ns = (period_ns * brightness) / LED_FULL;
+
+ if (period_ns < duty_ns && duty_ns != 0)
+ period_ns = duty_ns + 1;
+ }
+ dev_dbg(led->chip->dev, "PWM settings for %s led: period = %dns, duty = %dns\n",
+ led->cdev.name, period_ns, duty_ns);
+
+ led->pwm_setting.duty_ns = duty_ns;
+ led->pwm_setting.period_ns = period_ns;
+
+ rc = __tri_led_set(led);
+ if (rc < 0) {
+ dev_err(led->chip->dev, "__tri_led_set %s failed, rc=%d\n",
+ led->cdev.name, rc);
+ return rc;
+ }
+
+ if (led->led_setting.blink) {
+ led->cdev.brightness = LED_FULL;
+ led->blinking = true;
+ } else {
+ led->cdev.brightness = led->led_setting.brightness;
+ led->blinking = false;
+ }
+
+ return rc;
+}
+
+static int qpnp_tri_led_set_brightness(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct qpnp_led_dev *led =
+ container_of(led_cdev, struct qpnp_led_dev, cdev);
+ int rc = 0;
+
+ mutex_lock(&led->lock);
+ if (brightness > LED_FULL)
+ brightness = LED_FULL;
+
+ if (brightness == led->led_setting.brightness &&
+ !led->blinking) {
+ mutex_unlock(&led->lock);
+ return 0;
+ }
+
+ led->led_setting.brightness = brightness;
+ if (!!brightness)
+ led->led_setting.off_ms = 0;
+ else
+ led->led_setting.on_ms = 0;
+ led->led_setting.blink = false;
+
+ rc = qpnp_tri_led_set(led);
+ if (rc)
+ dev_err(led->chip->dev, "Set led failed for %s, rc=%d\n",
+ led->label, rc);
+
+ mutex_unlock(&led->lock);
+
+ return rc;
+}
+
+static enum led_brightness qpnp_tri_led_get_brightness(
+ struct led_classdev *led_cdev)
+{
+ return led_cdev->brightness;
+}
+
+static int qpnp_tri_led_set_blink(struct led_classdev *led_cdev,
+ unsigned long *on_ms, unsigned long *off_ms)
+{
+ struct qpnp_led_dev *led =
+ container_of(led_cdev, struct qpnp_led_dev, cdev);
+ int rc = 0;
+
+ mutex_lock(&led->lock);
+ if (led->blinking && *on_ms == led->led_setting.on_ms &&
+ *off_ms == led->led_setting.off_ms) {
+ dev_dbg(led_cdev->dev, "Ignore, on/off setting is not changed: on %lums, off %lums\n",
+ *on_ms, *off_ms);
+ mutex_unlock(&led->lock);
+ return 0;
+ }
+
+ if (*on_ms == 0) {
+ led->led_setting.blink = false;
+ led->led_setting.brightness = LED_OFF;
+ } else if (*off_ms == 0) {
+ led->led_setting.blink = false;
+ led->led_setting.brightness = led->cdev.brightness;
+ } else {
+ led->led_setting.on_ms = *on_ms;
+ led->led_setting.off_ms = *off_ms;
+ led->led_setting.blink = true;
+ }
+
+ rc = qpnp_tri_led_set(led);
+ if (rc)
+ dev_err(led->chip->dev, "Set led failed for %s, rc=%d\n",
+ led->label, rc);
+
+ mutex_unlock(&led->lock);
+ return rc;
+}
+
+static int qpnp_tri_led_register(struct qpnp_tri_led_chip *chip)
+{
+ struct qpnp_led_dev *led;
+ int rc, i, j;
+
+ for (i = 0; i < chip->num_leds; i++) {
+ led = &chip->leds[i];
+ mutex_init(&led->lock);
+ led->cdev.name = led->label;
+ led->cdev.max_brightness = LED_FULL;
+ led->cdev.brightness_set_blocking = qpnp_tri_led_set_brightness;
+ led->cdev.brightness_get = qpnp_tri_led_get_brightness;
+ led->cdev.blink_set = qpnp_tri_led_set_blink;
+ led->cdev.default_trigger = led->default_trigger;
+ led->cdev.brightness = LED_OFF;
+ led->cdev.blink_delay_on = LED_BLINK_ON_MS;
+ led->cdev.blink_delay_off = LED_BLINK_OFF_MS;
+
+ rc = devm_led_classdev_register(chip->dev, &led->cdev);
+ if (rc < 0) {
+ dev_err(chip->dev, "%s led class device registering failed, rc=%d\n",
+ led->label, rc);
+ goto destroy;
+ }
+ }
+
+ return 0;
+destroy:
+ for (j = 0; j <= i; j++)
+ mutex_destroy(&chip->leds[i].lock);
+
+ return rc;
+}
+
+static int qpnp_tri_led_hw_init(struct qpnp_tri_led_chip *chip)
+{
+ int rc = 0;
+ u8 val;
+
+ rc = qpnp_tri_led_read(chip, TRILED_REG_TYPE, &val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Read REG_TYPE failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (val != TRILED_TYPE) {
+ dev_err(chip->dev, "invalid subtype(%d)\n", val);
+ return -ENODEV;
+ }
+
+ rc = qpnp_tri_led_read(chip, TRILED_REG_SUBTYPE, &val);
+ if (rc < 0) {
+ dev_err(chip->dev, "Read REG_SUBTYPE failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ chip->subtype = val;
+
+ return 0;
+}
+
+static int qpnp_tri_led_parse_dt(struct qpnp_tri_led_chip *chip)
+{
+ struct device_node *node = chip->dev->of_node, *child_node;
+ struct qpnp_led_dev *led;
+ struct pwm_args pargs;
+ const __be32 *addr;
+ int rc, id, i = 0;
+
+ addr = of_get_address(chip->dev->of_node, 0, NULL, NULL);
+ if (!addr) {
+ dev_err(chip->dev, "Getting address failed\n");
+ return -EINVAL;
+ }
+ chip->reg_base = be32_to_cpu(addr[0]);
+
+ chip->num_leds = of_get_available_child_count(node);
+ if (chip->num_leds == 0) {
+ dev_err(chip->dev, "No led child node defined\n");
+ return -ENODEV;
+ }
+
+ if (chip->num_leds > TRILED_NUM_MAX) {
+ dev_err(chip->dev, "can't support %d leds(max %d)\n",
+ chip->num_leds, TRILED_NUM_MAX);
+ return -EINVAL;
+ }
+
+ chip->leds = devm_kcalloc(chip->dev, chip->num_leds,
+ sizeof(struct qpnp_led_dev), GFP_KERNEL);
+ if (!chip->leds)
+ return -ENOMEM;
+
+ for_each_available_child_of_node(node, child_node) {
+ rc = of_property_read_u32(child_node, "led-sources", &id);
+ if (rc) {
+ dev_err(chip->dev, "Get led-sources failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ if (id >= TRILED_NUM_MAX) {
+ dev_err(chip->dev, "only support 0~%d current source\n",
+ TRILED_NUM_MAX - 1);
+ return -EINVAL;
+ }
+
+ led = &chip->leds[i++];
+ led->chip = chip;
+ led->id = id;
+ led->label =
+ of_get_property(child_node, "label", NULL) ? :
+ child_node->name;
+
+ led->pwm_dev =
+ devm_of_pwm_get(chip->dev, child_node, NULL);
+ if (IS_ERR(led->pwm_dev)) {
+ rc = PTR_ERR(led->pwm_dev);
+ if (rc != -EPROBE_DEFER)
+ dev_err(chip->dev, "Get pwm device for %s led failed, rc=%d\n",
+ led->label, rc);
+ return rc;
+ }
+
+ pwm_get_args(led->pwm_dev, &pargs);
+ if (pargs.period == 0)
+ led->pwm_setting.pre_period_ns = PWM_PERIOD_DEFAULT_NS;
+ else
+ led->pwm_setting.pre_period_ns = pargs.period;
+
+ led->default_trigger = of_get_property(child_node,
+ "linux,default-trigger", NULL);
+ }
+
+ return rc;
+}
+
+static int qpnp_tri_led_probe(struct platform_device *pdev)
+{
+ struct qpnp_tri_led_chip *chip;
+ int rc = 0;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = &pdev->dev;
+ chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
+ if (!chip->regmap) {
+ dev_err(chip->dev, "Getting regmap failed\n");
+ return -EINVAL;
+ }
+
+ rc = qpnp_tri_led_parse_dt(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ mutex_init(&chip->bus_lock);
+
+ rc = qpnp_tri_led_hw_init(chip);
+ if (rc) {
+ dev_err(chip->dev, "HW initialization failed, rc=%d\n", rc);
+ goto destroy;
+ }
+
+ dev_set_drvdata(chip->dev, chip);
+ rc = qpnp_tri_led_register(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Registering LED class devices failed, rc=%d\n",
+ rc);
+ goto destroy;
+ }
+
+ dev_dbg(chip->dev, "Tri-led module with subtype 0x%x is detected\n",
+ chip->subtype);
+ return 0;
+destroy:
+ mutex_destroy(&chip->bus_lock);
+ dev_set_drvdata(chip->dev, NULL);
+
+ return rc;
+}
+
+static int qpnp_tri_led_remove(struct platform_device *pdev)
+{
+ int i;
+ struct qpnp_tri_led_chip *chip = dev_get_drvdata(&pdev->dev);
+
+ mutex_destroy(&chip->bus_lock);
+ for (i = 0; i < chip->num_leds; i++)
+ mutex_destroy(&chip->leds[i].lock);
+ dev_set_drvdata(chip->dev, NULL);
+ return 0;
+}
+
+static const struct of_device_id qpnp_tri_led_of_match[] = {
+ { .compatible = "qcom,tri-led",},
+ { },
+};
+
+static struct platform_driver qpnp_tri_led_driver = {
+ .driver = {
+ .name = "qcom,tri-led",
+ .of_match_table = qpnp_tri_led_of_match,
+ },
+ .probe = qpnp_tri_led_probe,
+ .remove = qpnp_tri_led_remove,
+};
+module_platform_driver(qpnp_tri_led_driver);
+
+MODULE_DESCRIPTION("QTI TRI_LED driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("leds:qpnp-tri-led");
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index 7643f72..3ec647e 100644
--- a/drivers/md/dm-bufio.c
+++ b/drivers/md/dm-bufio.c
@@ -1554,7 +1554,8 @@
int l;
struct dm_buffer *b, *tmp;
unsigned long freed = 0;
- unsigned long count = nr_to_scan;
+ unsigned long count = c->n_buffers[LIST_CLEAN] +
+ c->n_buffers[LIST_DIRTY];
unsigned long retain_target = get_retain_buffers(c);
for (l = 0; l < LIST_SIZE; l++) {
@@ -1591,6 +1592,7 @@
{
struct dm_bufio_client *c;
unsigned long count;
+ unsigned long retain_target;
c = container_of(shrink, struct dm_bufio_client, shrinker);
if (sc->gfp_mask & __GFP_FS)
@@ -1599,8 +1601,9 @@
return 0;
count = c->n_buffers[LIST_CLEAN] + c->n_buffers[LIST_DIRTY];
+ retain_target = get_retain_buffers(c);
dm_bufio_unlock(c);
- return count;
+ return (count < retain_target) ? 0 : (count - retain_target);
}
/*
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index 03dfde6..44cc7dc 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, 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
@@ -39,6 +39,9 @@
case HFI_ERR_SYS_FATAL:
vidc_err = VIDC_ERR_HW_FATAL;
break;
+ case HFI_ERR_SYS_NOC_ERROR:
+ vidc_err = VIDC_ERR_NOC_ERROR;
+ break;
case HFI_ERR_SYS_VERSION_MISMATCH:
case HFI_ERR_SYS_INVALID_PARAMETER:
case HFI_ERR_SYS_SESSION_ID_OUT_OF_RANGE:
@@ -316,11 +319,14 @@
return 0;
}
-static int hfi_process_sys_error(u32 device_id, struct msm_vidc_cb_info *info)
+static int hfi_process_sys_error(u32 device_id,
+ struct hfi_msg_event_notify_packet *pkt,
+ struct msm_vidc_cb_info *info)
{
struct msm_vidc_cb_cmd_done cmd_done = {0};
cmd_done.device_id = device_id;
+ cmd_done.status = hfi_map_err_status(pkt->event_data1);
info->response_type = HAL_SYS_ERROR;
info->response.cmd = cmd_done;
@@ -373,7 +379,7 @@
case HFI_EVENT_SYS_ERROR:
dprintk(VIDC_ERR, "HFI_EVENT_SYS_ERROR: %d, %#x\n",
pkt->event_data1, pkt->event_data2);
- return hfi_process_sys_error(device_id, info);
+ return hfi_process_sys_error(device_id, pkt, info);
case HFI_EVENT_SESSION_ERROR:
dprintk(VIDC_INFO, "HFI_EVENT_SESSION_ERROR[%#x]\n",
pkt->session_id);
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
index fa40091..42bf1ba 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, 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
@@ -39,6 +39,8 @@
static inline struct msm_vidc_inst *get_vidc_inst(struct file *filp, void *fh)
{
+ if (!filp->private_data)
+ return NULL;
return container_of(filp->private_data,
struct msm_vidc_inst, event_handler);
}
@@ -74,6 +76,7 @@
vidc_inst = get_vidc_inst(filp, NULL);
rc = msm_vidc_close(vidc_inst);
+ filp->private_data = NULL;
trace_msm_v4l2_vidc_close_end("msm_v4l2_close end");
return rc;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 360d2df..fd0fd39 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -35,6 +35,8 @@
V4L2_EVENT_MSM_VIDC_RELEASE_BUFFER_REFERENCE
#define L_MODE V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY
+#define TRIGGER_SSR_LOCK_RETRIES 5
+
const char *const mpeg_video_vidc_extradata[] = {
"Extradata none",
"Extradata MB Quantization",
@@ -2207,6 +2209,10 @@
}
/* handle the hw error before core released to get full debug info */
msm_vidc_handle_hw_error(core);
+ if (response->status == VIDC_ERR_NOC_ERROR) {
+ dprintk(VIDC_WARN, "Got NOC error");
+ MSM_VIDC_ERROR(true);
+ }
dprintk(VIDC_DBG, "Calling core_release\n");
rc = call_hfi_op(hdev, core_release, hdev->hfi_device_data);
if (rc) {
@@ -5305,6 +5311,7 @@
{
int rc = 0;
struct hfi_device *hdev;
+ int try_lock_counter = TRIGGER_SSR_LOCK_RETRIES;
if (!core || !core->device) {
dprintk(VIDC_WARN, "Invalid parameters: %pK\n", core);
@@ -5312,7 +5319,13 @@
}
hdev = core->device;
- mutex_lock(&core->lock);
+ while (try_lock_counter) {
+ if (mutex_trylock(&core->lock))
+ break;
+ try_lock_counter--;
+ if (!try_lock_counter)
+ return -EBUSY;
+ }
if (core->state == VIDC_CORE_INIT_DONE) {
/*
* In current implementation user-initiated SSR triggers
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
index 215bb78..74fa3d5 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, 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
@@ -131,7 +131,7 @@
static ssize_t trigger_ssr_write(struct file *filp, const char __user *buf,
size_t count, loff_t *ppos) {
unsigned long ssr_trigger_val = 0;
- int rc = 0;
+ int rc = 0, ret = 0;
struct msm_vidc_core *core = filp->private_data;
size_t size = MAX_SSR_STRING_LEN;
char kbuf[MAX_SSR_STRING_LEN + 1] = {0};
@@ -156,8 +156,8 @@
dprintk(VIDC_WARN, "returning error err %d\n", rc);
rc = -EINVAL;
} else {
- msm_vidc_trigger_ssr(core, ssr_trigger_val);
- rc = count;
+ ret = msm_vidc_trigger_ssr(core, ssr_trigger_val);
+ rc = (ret == -EBUSY ? ret : count);
}
exit:
return rc;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
index b1a240d..530fe3a 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, 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
@@ -992,10 +992,11 @@
if (core->smmu_fault_handled) {
if (core->resources.non_fatal_pagefaults) {
- msm_vidc_noc_error_info(core);
- MSM_VIDC_ERROR(true);
+ dprintk(VIDC_ERR,
+ "%s: non-fatal pagefault address: %lx\n",
+ __func__, iova);
+ return 0;
}
- return -ENOSYS;
}
dprintk(VIDC_ERR, "%s - faulting address: %lx\n", __func__, iova);
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 2260b55..54cbdfc 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, 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
@@ -88,6 +88,7 @@
VIDC_ERR_TIMEOUT,
VIDC_ERR_CMDQFULL,
VIDC_ERR_START_CODE_NOT_FOUND,
+ VIDC_ERR_NOC_ERROR,
VIDC_ERR_CLIENT_PRESENT = 0x90000001,
VIDC_ERR_CLIENT_FATAL,
VIDC_ERR_CMD_QUEUE_FULL,
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index ca6d803..ea8cf1a 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, 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
@@ -45,7 +45,7 @@
#define HFI_ERR_SYS_SESSION_IN_USE (HFI_COMMON_BASE + 0x7)
#define HFI_ERR_SYS_SESSION_ID_OUT_OF_RANGE (HFI_COMMON_BASE + 0x8)
#define HFI_ERR_SYS_UNSUPPORTED_DOMAIN (HFI_COMMON_BASE + 0x9)
-
+#define HFI_ERR_SYS_NOC_ERROR (HFI_COMMON_BASE + 0x11)
#define HFI_ERR_SESSION_FATAL (HFI_COMMON_BASE + 0x1001)
#define HFI_ERR_SESSION_INVALID_PARAMETER (HFI_COMMON_BASE + 0x1002)
#define HFI_ERR_SESSION_BAD_POINTER (HFI_COMMON_BASE + 0x1003)
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 367c84f..0d6f263 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -2,7 +2,7 @@
* drivers/mmc/host/sdhci-msm.c - Qualcomm Technologies, Inc. MSM SDHCI Platform
* driver source file
*
- * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2018, 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
@@ -39,6 +39,7 @@
#include <linux/iopoll.h>
#include <linux/msm-bus.h>
#include <linux/pm_runtime.h>
+#include <linux/nvmem-consumer.h>
#include <trace/events/mmc.h>
#include "sdhci-msm.h"
@@ -1885,6 +1886,65 @@
}
}
+#ifdef CONFIG_NVMEM
+/* Parse qfprom data for deciding on errata work-arounds */
+static long qfprom_read(struct device *dev, const char *name)
+{
+ struct nvmem_cell *cell;
+ ssize_t len = 0;
+ u32 *buf, val = 0;
+ long err = 0;
+
+ cell = nvmem_cell_get(dev, name);
+ if (IS_ERR(cell)) {
+ err = PTR_ERR(cell);
+ dev_err(dev, "failed opening nvmem cell err : %ld\n", err);
+ /* If entry does not exist, then that is not an error */
+ if (err == -ENOENT)
+ err = 0;
+ return err;
+ }
+
+ buf = (u32 *)nvmem_cell_read(cell, &len);
+ if (IS_ERR(buf) || !len) {
+ dev_err(dev, "Failed reading nvmem cell, err: %u, bytes fetched: %zd\n",
+ *buf, len);
+ if (!IS_ERR(buf)) {
+ kfree(buf);
+ err = -EINVAL;
+ } else {
+ err = PTR_ERR(buf);
+ }
+ } else {
+ val = *buf;
+ kfree(buf);
+ }
+
+ nvmem_cell_put(cell);
+ return err ? err : (long) val;
+}
+
+/* Reads the SoC version */
+static int sdhci_msm_get_socrev(struct device *dev,
+ struct sdhci_msm_host *msm_host)
+{
+
+ msm_host->soc_min_rev = qfprom_read(dev, "minor_rev");
+
+ if (msm_host->soc_min_rev < 0)
+ dev_err(dev, "failed getting soc_min_rev, err : %d\n",
+ msm_host->soc_min_rev);
+ return msm_host->soc_min_rev;
+}
+#else
+/* Reads the SoC version */
+static int sdhci_msm_get_socrev(struct device *dev,
+ struct sdhci_msm_host *msm_host)
+{
+ return 0;
+}
+#endif
+
/* Parse platform data */
static
struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev,
@@ -2062,6 +2122,13 @@
if (!of_property_read_u32(np, "qcom,ddr-config", &pdata->ddr_config))
pdata->rclk_wa = true;
+ /*
+ * rclk_wa is not required if soc version is mentioned and
+ * is not base version.
+ */
+ if (msm_host->soc_min_rev != 0)
+ pdata->rclk_wa = false;
+
return pdata;
out:
return NULL;
@@ -4530,6 +4597,12 @@
msm_host->mmc = host->mmc;
msm_host->pdev = pdev;
+ ret = sdhci_msm_get_socrev(&pdev->dev, msm_host);
+ if (ret == -EPROBE_DEFER) {
+ dev_err(&pdev->dev, "SoC version rd: fail: defer for now\n");
+ goto pltfm_free;
+ }
+
/* get the ice device vops if present */
ret = sdhci_msm_ice_get_dev(host);
if (ret == -EPROBE_DEFER) {
diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h
index 7c737cc..9c2442d 100644
--- a/drivers/mmc/host/sdhci-msm.h
+++ b/drivers/mmc/host/sdhci-msm.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, 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
@@ -255,6 +255,7 @@
bool core_3_0v_support;
bool pltfm_init_done;
struct sdhci_msm_regs_restore regs_restore;
+ int soc_min_rev;
};
extern char *saved_command_line;
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index b121bf4..3b8911c 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -950,6 +950,7 @@
switch (command) {
case NAND_CMD_READ0:
+ case NAND_CMD_READOOB:
case NAND_CMD_PAGEPROG:
info->use_ecc = 1;
break;
diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c
index eea9aea..5d50123 100644
--- a/drivers/net/can/usb/gs_usb.c
+++ b/drivers/net/can/usb/gs_usb.c
@@ -449,7 +449,7 @@
dev_err(netdev->dev.parent, "Couldn't set bittimings (err=%d)",
rc);
- return rc;
+ return (rc > 0) ? 0 : rc;
}
static void gs_usb_xmit_callback(struct urb *urb)
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index edae2dc..bb22d32 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -14226,7 +14226,9 @@
/* Reset PHY, otherwise the read DMA engine will be in a mode that
* breaks all requests to 256 bytes.
*/
- if (tg3_asic_rev(tp) == ASIC_REV_57766)
+ if (tg3_asic_rev(tp) == ASIC_REV_57766 ||
+ tg3_asic_rev(tp) == ASIC_REV_5717 ||
+ tg3_asic_rev(tp) == ASIC_REV_5719)
reset_phy = true;
err = tg3_restart_hw(tp, reset_phy);
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 849b871..9170918 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -172,10 +172,12 @@
#endif /* CONFIG_M5272 */
/* The FEC stores dest/src/type/vlan, data, and checksum for receive packets.
+ *
+ * 2048 byte skbufs are allocated. However, alignment requirements
+ * varies between FEC variants. Worst case is 64, so round down by 64.
*/
-#define PKT_MAXBUF_SIZE 1522
+#define PKT_MAXBUF_SIZE (round_down(2048 - 64, 64))
#define PKT_MINBUF_SIZE 64
-#define PKT_MAXBLR_SIZE 1536
/* FEC receive acceleration */
#define FEC_RACC_IPDIS (1 << 1)
@@ -813,6 +815,12 @@
for (i = 0; i < txq->bd.ring_size; i++) {
/* Initialize the BD for every fragment in the page. */
bdp->cbd_sc = cpu_to_fec16(0);
+ if (bdp->cbd_bufaddr &&
+ !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr)))
+ dma_unmap_single(&fep->pdev->dev,
+ fec32_to_cpu(bdp->cbd_bufaddr),
+ fec16_to_cpu(bdp->cbd_datlen),
+ DMA_TO_DEVICE);
if (txq->tx_skbuff[i]) {
dev_kfree_skb_any(txq->tx_skbuff[i]);
txq->tx_skbuff[i] = NULL;
@@ -847,7 +855,7 @@
for (i = 0; i < fep->num_rx_queues; i++) {
rxq = fep->rx_queue[i];
writel(rxq->bd.dma, fep->hwp + FEC_R_DES_START(i));
- writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i));
+ writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i));
/* enable DMA1/2 */
if (i)
diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c
index f3aaca7..8a48656 100644
--- a/drivers/net/ethernet/intel/e1000e/ich8lan.c
+++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c
@@ -1364,6 +1364,9 @@
* Checks to see of the link status of the hardware has changed. If a
* change in link status has been detected, then we read the PHY registers
* to get the current speed/duplex if link exists.
+ *
+ * Returns a negative error code (-E1000_ERR_*) or 0 (link down) or 1 (link
+ * up).
**/
static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
{
@@ -1379,7 +1382,7 @@
* Change or Rx Sequence Error interrupt.
*/
if (!mac->get_link_status)
- return 0;
+ return 1;
/* First we want to see if the MII Status Register reports
* link. If so, then we want to get the current speed/duplex
@@ -1611,10 +1614,12 @@
* different link partner.
*/
ret_val = e1000e_config_fc_after_link_up(hw);
- if (ret_val)
+ if (ret_val) {
e_dbg("Error configuring flow control\n");
+ return ret_val;
+ }
- return ret_val;
+ return 1;
}
static s32 e1000_get_variants_ich8lan(struct e1000_adapter *adapter)
diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c
index a0d1b08..7aeb7fe 100644
--- a/drivers/net/ethernet/marvell/mvmdio.c
+++ b/drivers/net/ethernet/marvell/mvmdio.c
@@ -232,7 +232,8 @@
dev->regs + MVMDIO_ERR_INT_MASK);
} else if (dev->err_interrupt == -EPROBE_DEFER) {
- return -EPROBE_DEFER;
+ ret = -EPROBE_DEFER;
+ goto out_mdio;
}
mutex_init(&dev->lock);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
index f7fabec..4c3f1cb 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
@@ -367,7 +367,7 @@
case MLX5_CMD_OP_QUERY_VPORT_COUNTER:
case MLX5_CMD_OP_ALLOC_Q_COUNTER:
case MLX5_CMD_OP_QUERY_Q_COUNTER:
- case MLX5_CMD_OP_SET_RATE_LIMIT:
+ case MLX5_CMD_OP_SET_PP_RATE_LIMIT:
case MLX5_CMD_OP_QUERY_RATE_LIMIT:
case MLX5_CMD_OP_ALLOC_PD:
case MLX5_CMD_OP_ALLOC_UAR:
@@ -502,7 +502,7 @@
MLX5_COMMAND_STR_CASE(ALLOC_Q_COUNTER);
MLX5_COMMAND_STR_CASE(DEALLOC_Q_COUNTER);
MLX5_COMMAND_STR_CASE(QUERY_Q_COUNTER);
- MLX5_COMMAND_STR_CASE(SET_RATE_LIMIT);
+ MLX5_COMMAND_STR_CASE(SET_PP_RATE_LIMIT);
MLX5_COMMAND_STR_CASE(QUERY_RATE_LIMIT);
MLX5_COMMAND_STR_CASE(ALLOC_PD);
MLX5_COMMAND_STR_CASE(DEALLOC_PD);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 9d37229..38981db 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -3038,6 +3038,7 @@
struct sk_buff *skb,
netdev_features_t features)
{
+ unsigned int offset = 0;
struct udphdr *udph;
u16 proto;
u16 port = 0;
@@ -3047,7 +3048,7 @@
proto = ip_hdr(skb)->protocol;
break;
case htons(ETH_P_IPV6):
- proto = ipv6_hdr(skb)->nexthdr;
+ proto = ipv6_find_hdr(skb, &offset, -1, NULL, NULL);
break;
default:
goto out;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qp.c b/drivers/net/ethernet/mellanox/mlx5/core/qp.c
index d0a4005..9346f39 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/qp.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/qp.c
@@ -303,8 +303,8 @@
err_cmd:
memset(din, 0, sizeof(din));
memset(dout, 0, sizeof(dout));
- MLX5_SET(destroy_qp_in, in, opcode, MLX5_CMD_OP_DESTROY_QP);
- MLX5_SET(destroy_qp_in, in, qpn, qp->qpn);
+ MLX5_SET(destroy_qp_in, din, opcode, MLX5_CMD_OP_DESTROY_QP);
+ MLX5_SET(destroy_qp_in, din, qpn, qp->qpn);
mlx5_cmd_exec(dev, din, sizeof(din), dout, sizeof(dout));
return err;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/rl.c b/drivers/net/ethernet/mellanox/mlx5/core/rl.c
index 104902a..2be9ec5 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/rl.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/rl.c
@@ -60,16 +60,16 @@
return ret_entry;
}
-static int mlx5_set_rate_limit_cmd(struct mlx5_core_dev *dev,
+static int mlx5_set_pp_rate_limit_cmd(struct mlx5_core_dev *dev,
u32 rate, u16 index)
{
- u32 in[MLX5_ST_SZ_DW(set_rate_limit_in)] = {0};
- u32 out[MLX5_ST_SZ_DW(set_rate_limit_out)] = {0};
+ u32 in[MLX5_ST_SZ_DW(set_pp_rate_limit_in)] = {0};
+ u32 out[MLX5_ST_SZ_DW(set_pp_rate_limit_out)] = {0};
- MLX5_SET(set_rate_limit_in, in, opcode,
- MLX5_CMD_OP_SET_RATE_LIMIT);
- MLX5_SET(set_rate_limit_in, in, rate_limit_index, index);
- MLX5_SET(set_rate_limit_in, in, rate_limit, rate);
+ MLX5_SET(set_pp_rate_limit_in, in, opcode,
+ MLX5_CMD_OP_SET_PP_RATE_LIMIT);
+ MLX5_SET(set_pp_rate_limit_in, in, rate_limit_index, index);
+ MLX5_SET(set_pp_rate_limit_in, in, rate_limit, rate);
return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
}
@@ -108,7 +108,7 @@
entry->refcount++;
} else {
/* new rate limit */
- err = mlx5_set_rate_limit_cmd(dev, rate, entry->index);
+ err = mlx5_set_pp_rate_limit_cmd(dev, rate, entry->index);
if (err) {
mlx5_core_err(dev, "Failed configuring rate: %u (%d)\n",
rate, err);
@@ -144,7 +144,7 @@
entry->refcount--;
if (!entry->refcount) {
/* need to remove rate */
- mlx5_set_rate_limit_cmd(dev, 0, entry->index);
+ mlx5_set_pp_rate_limit_cmd(dev, 0, entry->index);
entry->rate = 0;
}
@@ -197,8 +197,8 @@
/* Clear all configured rates */
for (i = 0; i < table->max_size; i++)
if (table->rl_entry[i].rate)
- mlx5_set_rate_limit_cmd(dev, 0,
- table->rl_entry[i].index);
+ mlx5_set_pp_rate_limit_cmd(dev, 0,
+ table->rl_entry[i].index);
kfree(dev->priv.rl_table.rl_entry);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c
index 07a9ba6..2f74953 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.c
@@ -71,9 +71,9 @@
struct mlx5e_vxlan_db *vxlan_db = &priv->vxlan;
struct mlx5e_vxlan *vxlan;
- spin_lock(&vxlan_db->lock);
+ spin_lock_bh(&vxlan_db->lock);
vxlan = radix_tree_lookup(&vxlan_db->tree, port);
- spin_unlock(&vxlan_db->lock);
+ spin_unlock_bh(&vxlan_db->lock);
return vxlan;
}
@@ -88,8 +88,12 @@
struct mlx5e_vxlan *vxlan;
int err;
- if (mlx5e_vxlan_lookup_port(priv, port))
+ mutex_lock(&priv->state_lock);
+ vxlan = mlx5e_vxlan_lookup_port(priv, port);
+ if (vxlan) {
+ atomic_inc(&vxlan->refcount);
goto free_work;
+ }
if (mlx5e_vxlan_core_add_port_cmd(priv->mdev, port))
goto free_work;
@@ -99,10 +103,11 @@
goto err_delete_port;
vxlan->udp_port = port;
+ atomic_set(&vxlan->refcount, 1);
- spin_lock_irq(&vxlan_db->lock);
+ spin_lock_bh(&vxlan_db->lock);
err = radix_tree_insert(&vxlan_db->tree, vxlan->udp_port, vxlan);
- spin_unlock_irq(&vxlan_db->lock);
+ spin_unlock_bh(&vxlan_db->lock);
if (err)
goto err_free;
@@ -113,35 +118,39 @@
err_delete_port:
mlx5e_vxlan_core_del_port_cmd(priv->mdev, port);
free_work:
+ mutex_unlock(&priv->state_lock);
kfree(vxlan_work);
}
-static void __mlx5e_vxlan_core_del_port(struct mlx5e_priv *priv, u16 port)
-{
- struct mlx5e_vxlan_db *vxlan_db = &priv->vxlan;
- struct mlx5e_vxlan *vxlan;
-
- spin_lock_irq(&vxlan_db->lock);
- vxlan = radix_tree_delete(&vxlan_db->tree, port);
- spin_unlock_irq(&vxlan_db->lock);
-
- if (!vxlan)
- return;
-
- mlx5e_vxlan_core_del_port_cmd(priv->mdev, vxlan->udp_port);
-
- kfree(vxlan);
-}
-
static void mlx5e_vxlan_del_port(struct work_struct *work)
{
struct mlx5e_vxlan_work *vxlan_work =
container_of(work, struct mlx5e_vxlan_work, work);
- struct mlx5e_priv *priv = vxlan_work->priv;
+ struct mlx5e_priv *priv = vxlan_work->priv;
+ struct mlx5e_vxlan_db *vxlan_db = &priv->vxlan;
u16 port = vxlan_work->port;
+ struct mlx5e_vxlan *vxlan;
+ bool remove = false;
- __mlx5e_vxlan_core_del_port(priv, port);
+ mutex_lock(&priv->state_lock);
+ spin_lock_bh(&vxlan_db->lock);
+ vxlan = radix_tree_lookup(&vxlan_db->tree, port);
+ if (!vxlan)
+ goto out_unlock;
+ if (atomic_dec_and_test(&vxlan->refcount)) {
+ radix_tree_delete(&vxlan_db->tree, port);
+ remove = true;
+ }
+
+out_unlock:
+ spin_unlock_bh(&vxlan_db->lock);
+
+ if (remove) {
+ mlx5e_vxlan_core_del_port_cmd(priv->mdev, port);
+ kfree(vxlan);
+ }
+ mutex_unlock(&priv->state_lock);
kfree(vxlan_work);
}
@@ -171,12 +180,11 @@
struct mlx5e_vxlan *vxlan;
unsigned int port = 0;
- spin_lock_irq(&vxlan_db->lock);
+ /* Lockless since we are the only radix-tree consumers, wq is disabled */
while (radix_tree_gang_lookup(&vxlan_db->tree, (void **)&vxlan, port, 1)) {
port = vxlan->udp_port;
- spin_unlock_irq(&vxlan_db->lock);
- __mlx5e_vxlan_core_del_port(priv, (u16)port);
- spin_lock_irq(&vxlan_db->lock);
+ radix_tree_delete(&vxlan_db->tree, port);
+ mlx5e_vxlan_core_del_port_cmd(priv->mdev, port);
+ kfree(vxlan);
}
- spin_unlock_irq(&vxlan_db->lock);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h
index 5def12c..5ef6ae7 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/vxlan.h
@@ -36,6 +36,7 @@
#include "en.h"
struct mlx5e_vxlan {
+ atomic_t refcount;
u16 udp_port;
};
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index 9e31a33..8aa91dd 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -1328,9 +1328,9 @@
static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh,
bool removing)
{
- if (!removing && !nh->should_offload)
+ if (!removing)
nh->should_offload = 1;
- else if (removing && nh->offloaded)
+ else
nh->should_offload = 0;
nh->update = 1;
}
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index 2140ded..b6816ae 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -3087,18 +3087,37 @@
/* ioremap the TSU registers */
if (mdp->cd->tsu) {
struct resource *rtsu;
+
rtsu = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- mdp->tsu_addr = devm_ioremap_resource(&pdev->dev, rtsu);
- if (IS_ERR(mdp->tsu_addr)) {
- ret = PTR_ERR(mdp->tsu_addr);
+ if (!rtsu) {
+ dev_err(&pdev->dev, "no TSU resource\n");
+ ret = -ENODEV;
+ goto out_release;
+ }
+ /* We can only request the TSU region for the first port
+ * of the two sharing this TSU for the probe to succeed...
+ */
+ if (devno % 2 == 0 &&
+ !devm_request_mem_region(&pdev->dev, rtsu->start,
+ resource_size(rtsu),
+ dev_name(&pdev->dev))) {
+ dev_err(&pdev->dev, "can't request TSU resource.\n");
+ ret = -EBUSY;
+ goto out_release;
+ }
+ mdp->tsu_addr = devm_ioremap(&pdev->dev, rtsu->start,
+ resource_size(rtsu));
+ if (!mdp->tsu_addr) {
+ dev_err(&pdev->dev, "TSU region ioremap() failed.\n");
+ ret = -ENOMEM;
goto out_release;
}
mdp->port = devno % 2;
ndev->features = NETIF_F_HW_VLAN_CTAG_FILTER;
}
- /* initialize first or needed device */
- if (!devno || pd->needs_init) {
+ /* Need to init only the first port of the two sharing a TSU */
+ if (devno % 2 == 0) {
if (mdp->cd->chip_reset)
mdp->cd->chip_reset(ndev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index adf61a7..98bbb91 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -280,8 +280,14 @@
bool stmmac_eee_init(struct stmmac_priv *priv)
{
unsigned long flags;
+ int interface = priv->plat->interface;
bool ret = false;
+ if ((interface != PHY_INTERFACE_MODE_MII) &&
+ (interface != PHY_INTERFACE_MODE_GMII) &&
+ !phy_interface_mode_is_rgmii(interface))
+ goto out;
+
/* Using PCS we cannot dial with the phy registers at this stage
* so we do not support extra feature like EEE.
*/
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index fbf5945..2032a6d 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -624,6 +624,7 @@
phydev->link = 0;
if (phydev->drv->config_intr && phy_interrupt_is_valid(phydev))
phydev->drv->config_intr(phydev);
+ return genphy_config_aneg(phydev);
}
return 0;
diff --git a/drivers/net/usb/cx82310_eth.c b/drivers/net/usb/cx82310_eth.c
index e221bfc..947bea8 100644
--- a/drivers/net/usb/cx82310_eth.c
+++ b/drivers/net/usb/cx82310_eth.c
@@ -293,12 +293,9 @@
{
int len = skb->len;
- if (skb_headroom(skb) < 2) {
- struct sk_buff *skb2 = skb_copy_expand(skb, 2, 0, flags);
+ if (skb_cow_head(skb, 2)) {
dev_kfree_skb_any(skb);
- skb = skb2;
- if (!skb)
- return NULL;
+ return NULL;
}
skb_push(skb, 2);
diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c
index f33460c..9c257ff 100644
--- a/drivers/net/usb/lan78xx.c
+++ b/drivers/net/usb/lan78xx.c
@@ -2419,14 +2419,9 @@
{
u32 tx_cmd_a, tx_cmd_b;
- if (skb_headroom(skb) < TX_OVERHEAD) {
- struct sk_buff *skb2;
-
- skb2 = skb_copy_expand(skb, TX_OVERHEAD, 0, flags);
+ if (skb_cow_head(skb, TX_OVERHEAD)) {
dev_kfree_skb_any(skb);
- skb = skb2;
- if (!skb)
- return NULL;
+ return NULL;
}
if (lan78xx_linearize(skb) < 0)
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 105fbfb..db65d9a 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -907,6 +907,7 @@
{QMI_FIXED_INTF(0x1199, 0x9079, 10)}, /* Sierra Wireless EM74xx */
{QMI_FIXED_INTF(0x1199, 0x907b, 8)}, /* Sierra Wireless EM74xx */
{QMI_FIXED_INTF(0x1199, 0x907b, 10)}, /* Sierra Wireless EM74xx */
+ {QMI_FIXED_INTF(0x1199, 0x9091, 8)}, /* Sierra Wireless EM7565 */
{QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */
{QMI_FIXED_INTF(0x1bbb, 0x0203, 2)}, /* Alcatel L800MA */
{QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */
diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c
index 9af9799..4cb9b11 100644
--- a/drivers/net/usb/smsc75xx.c
+++ b/drivers/net/usb/smsc75xx.c
@@ -2205,13 +2205,9 @@
{
u32 tx_cmd_a, tx_cmd_b;
- if (skb_headroom(skb) < SMSC75XX_TX_OVERHEAD) {
- struct sk_buff *skb2 =
- skb_copy_expand(skb, SMSC75XX_TX_OVERHEAD, 0, flags);
+ if (skb_cow_head(skb, SMSC75XX_TX_OVERHEAD)) {
dev_kfree_skb_any(skb);
- skb = skb2;
- if (!skb)
- return NULL;
+ return NULL;
}
tx_cmd_a = (u32)(skb->len & TX_CMD_A_LEN) | TX_CMD_A_FCS;
diff --git a/drivers/net/usb/sr9700.c b/drivers/net/usb/sr9700.c
index 4a1e9c4..aadfe1d 100644
--- a/drivers/net/usb/sr9700.c
+++ b/drivers/net/usb/sr9700.c
@@ -456,14 +456,9 @@
len = skb->len;
- if (skb_headroom(skb) < SR_TX_OVERHEAD) {
- struct sk_buff *skb2;
-
- skb2 = skb_copy_expand(skb, SR_TX_OVERHEAD, 0, flags);
+ if (skb_cow_head(skb, SR_TX_OVERHEAD)) {
dev_kfree_skb_any(skb);
- skb = skb2;
- if (!skb)
- return NULL;
+ return NULL;
}
__skb_push(skb, SR_TX_OVERHEAD);
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 0b4c156..ba1fe61 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -548,6 +548,11 @@
return IEEE80211_TKIP_IV_LEN;
case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
return IEEE80211_CCMP_HDR_LEN;
+ case HTT_RX_MPDU_ENCRYPT_AES_CCM256_WPA2:
+ return IEEE80211_CCMP_256_HDR_LEN;
+ case HTT_RX_MPDU_ENCRYPT_AES_GCMP_WPA2:
+ case HTT_RX_MPDU_ENCRYPT_AES_GCMP256_WPA2:
+ return IEEE80211_GCMP_HDR_LEN;
case HTT_RX_MPDU_ENCRYPT_WEP128:
case HTT_RX_MPDU_ENCRYPT_WAPI:
break;
@@ -573,6 +578,11 @@
return IEEE80211_TKIP_ICV_LEN;
case HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2:
return IEEE80211_CCMP_MIC_LEN;
+ case HTT_RX_MPDU_ENCRYPT_AES_CCM256_WPA2:
+ return IEEE80211_CCMP_256_MIC_LEN;
+ case HTT_RX_MPDU_ENCRYPT_AES_GCMP_WPA2:
+ case HTT_RX_MPDU_ENCRYPT_AES_GCMP256_WPA2:
+ return IEEE80211_GCMP_MIC_LEN;
case HTT_RX_MPDU_ENCRYPT_WEP128:
case HTT_RX_MPDU_ENCRYPT_WAPI:
break;
@@ -1024,9 +1034,21 @@
hdr = (void *)msdu->data;
/* Tail */
- if (status->flag & RX_FLAG_IV_STRIPPED)
+ if (status->flag & RX_FLAG_IV_STRIPPED) {
skb_trim(msdu, msdu->len -
ath10k_htt_rx_crypto_tail_len(ar, enctype));
+ } else {
+ /* MIC */
+ if ((status->flag & RX_FLAG_MIC_STRIPPED) &&
+ enctype == HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2)
+ skb_trim(msdu, msdu->len - 8);
+
+ /* ICV */
+ if (status->flag & RX_FLAG_ICV_STRIPPED &&
+ enctype != HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2)
+ skb_trim(msdu, msdu->len -
+ ath10k_htt_rx_crypto_tail_len(ar, enctype));
+ }
/* MMIC */
if ((status->flag & RX_FLAG_MMIC_STRIPPED) &&
@@ -1048,7 +1070,8 @@
static void ath10k_htt_rx_h_undecap_nwifi(struct ath10k *ar,
struct sk_buff *msdu,
struct ieee80211_rx_status *status,
- const u8 first_hdr[64])
+ const u8 first_hdr[64],
+ enum htt_rx_mpdu_encrypt_type enctype)
{
struct ieee80211_hdr *hdr;
struct htt_rx_desc *rxd;
@@ -1056,6 +1079,7 @@
u8 da[ETH_ALEN];
u8 sa[ETH_ALEN];
int l3_pad_bytes;
+ int bytes_aligned = ar->hw_params.decap_align_bytes;
/* Delivered decapped frame:
* [nwifi 802.11 header] <-- replaced with 802.11 hdr
@@ -1084,6 +1108,14 @@
/* push original 802.11 header */
hdr = (struct ieee80211_hdr *)first_hdr;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
+
+ if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
+ memcpy(skb_push(msdu,
+ ath10k_htt_rx_crypto_param_len(ar, enctype)),
+ (void *)hdr + round_up(hdr_len, bytes_aligned),
+ ath10k_htt_rx_crypto_param_len(ar, enctype));
+ }
+
memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
/* original 802.11 header has a different DA and in
@@ -1144,6 +1176,7 @@
u8 sa[ETH_ALEN];
int l3_pad_bytes;
struct htt_rx_desc *rxd;
+ int bytes_aligned = ar->hw_params.decap_align_bytes;
/* Delivered decapped frame:
* [eth header] <-- replaced with 802.11 hdr & rfc1042/llc
@@ -1172,6 +1205,14 @@
/* push original 802.11 header */
hdr = (struct ieee80211_hdr *)first_hdr;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
+
+ if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
+ memcpy(skb_push(msdu,
+ ath10k_htt_rx_crypto_param_len(ar, enctype)),
+ (void *)hdr + round_up(hdr_len, bytes_aligned),
+ ath10k_htt_rx_crypto_param_len(ar, enctype));
+ }
+
memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
/* original 802.11 header has a different DA and in
@@ -1185,12 +1226,14 @@
static void ath10k_htt_rx_h_undecap_snap(struct ath10k *ar,
struct sk_buff *msdu,
struct ieee80211_rx_status *status,
- const u8 first_hdr[64])
+ const u8 first_hdr[64],
+ enum htt_rx_mpdu_encrypt_type enctype)
{
struct ieee80211_hdr *hdr;
size_t hdr_len;
int l3_pad_bytes;
struct htt_rx_desc *rxd;
+ int bytes_aligned = ar->hw_params.decap_align_bytes;
/* Delivered decapped frame:
* [amsdu header] <-- replaced with 802.11 hdr
@@ -1206,6 +1249,14 @@
hdr = (struct ieee80211_hdr *)first_hdr;
hdr_len = ieee80211_hdrlen(hdr->frame_control);
+
+ if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
+ memcpy(skb_push(msdu,
+ ath10k_htt_rx_crypto_param_len(ar, enctype)),
+ (void *)hdr + round_up(hdr_len, bytes_aligned),
+ ath10k_htt_rx_crypto_param_len(ar, enctype));
+ }
+
memcpy(skb_push(msdu, hdr_len), hdr, hdr_len);
}
@@ -1240,13 +1291,15 @@
is_decrypted);
break;
case RX_MSDU_DECAP_NATIVE_WIFI:
- ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr);
+ ath10k_htt_rx_h_undecap_nwifi(ar, msdu, status, first_hdr,
+ enctype);
break;
case RX_MSDU_DECAP_ETHERNET2_DIX:
ath10k_htt_rx_h_undecap_eth(ar, msdu, status, first_hdr, enctype);
break;
case RX_MSDU_DECAP_8023_SNAP_LLC:
- ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr);
+ ath10k_htt_rx_h_undecap_snap(ar, msdu, status, first_hdr,
+ enctype);
break;
}
}
@@ -1289,7 +1342,8 @@
static void ath10k_htt_rx_h_mpdu(struct ath10k *ar,
struct sk_buff_head *amsdu,
- struct ieee80211_rx_status *status)
+ struct ieee80211_rx_status *status,
+ bool fill_crypt_header)
{
struct sk_buff *first;
struct sk_buff *last;
@@ -1299,7 +1353,6 @@
enum htt_rx_mpdu_encrypt_type enctype;
u8 first_hdr[64];
u8 *qos;
- size_t hdr_len;
bool has_fcs_err;
bool has_crypto_err;
bool has_tkip_err;
@@ -1324,15 +1377,17 @@
* decapped header. It'll be used for undecapping of each MSDU.
*/
hdr = (void *)rxd->rx_hdr_status;
- hdr_len = ieee80211_hdrlen(hdr->frame_control);
- memcpy(first_hdr, hdr, hdr_len);
+ memcpy(first_hdr, hdr, RX_HTT_HDR_STATUS_LEN);
/* Each A-MSDU subframe will use the original header as the base and be
* reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl.
*/
hdr = (void *)first_hdr;
- qos = ieee80211_get_qos_ctl(hdr);
- qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ qos = ieee80211_get_qos_ctl(hdr);
+ qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT;
+ }
/* Some attention flags are valid only in the last MSDU. */
last = skb_peek_tail(amsdu);
@@ -1379,9 +1434,14 @@
status->flag |= RX_FLAG_DECRYPTED;
if (likely(!is_mgmt))
- status->flag |= RX_FLAG_IV_STRIPPED |
- RX_FLAG_MMIC_STRIPPED;
-}
+ status->flag |= RX_FLAG_MMIC_STRIPPED;
+
+ if (fill_crypt_header)
+ status->flag |= RX_FLAG_MIC_STRIPPED |
+ RX_FLAG_ICV_STRIPPED;
+ else
+ status->flag |= RX_FLAG_IV_STRIPPED;
+ }
skb_queue_walk(amsdu, msdu) {
ath10k_htt_rx_h_csum_offload(msdu);
@@ -1397,6 +1457,9 @@
if (is_mgmt)
continue;
+ if (fill_crypt_header)
+ continue;
+
hdr = (void *)msdu->data;
hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED);
}
@@ -1407,6 +1470,9 @@
struct ieee80211_rx_status *status)
{
struct sk_buff *msdu;
+ struct sk_buff *first_subframe;
+
+ first_subframe = skb_peek(amsdu);
while ((msdu = __skb_dequeue(amsdu))) {
/* Setup per-MSDU flags */
@@ -1415,6 +1481,13 @@
else
status->flag |= RX_FLAG_AMSDU_MORE;
+ if (msdu == first_subframe) {
+ first_subframe = NULL;
+ status->flag &= ~RX_FLAG_ALLOW_SAME_PN;
+ } else {
+ status->flag |= RX_FLAG_ALLOW_SAME_PN;
+ }
+
ath10k_process_rx(ar, status, msdu);
}
}
@@ -1557,7 +1630,7 @@
ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status, 0xffff);
ath10k_htt_rx_h_unchain(ar, &amsdu, ret > 0);
ath10k_htt_rx_h_filter(ar, &amsdu, rx_status);
- ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status);
+ ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true);
ath10k_htt_rx_h_deliver(ar, &amsdu, rx_status);
return num_msdus;
@@ -1892,7 +1965,7 @@
num_msdus += skb_queue_len(&amsdu);
ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id);
ath10k_htt_rx_h_filter(ar, &amsdu, status);
- ath10k_htt_rx_h_mpdu(ar, &amsdu, status);
+ ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false);
ath10k_htt_rx_h_deliver(ar, &amsdu, status);
break;
case -EAGAIN:
diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h
index 034e7a5..e4878d0 100644
--- a/drivers/net/wireless/ath/ath10k/rx_desc.h
+++ b/drivers/net/wireless/ath/ath10k/rx_desc.h
@@ -239,6 +239,9 @@
HTT_RX_MPDU_ENCRYPT_WAPI = 5,
HTT_RX_MPDU_ENCRYPT_AES_CCM_WPA2 = 6,
HTT_RX_MPDU_ENCRYPT_NONE = 7,
+ HTT_RX_MPDU_ENCRYPT_AES_CCM256_WPA2 = 8,
+ HTT_RX_MPDU_ENCRYPT_AES_GCMP_WPA2 = 9,
+ HTT_RX_MPDU_ENCRYPT_AES_GCMP256_WPA2 = 10,
};
#define RX_MPDU_START_INFO0_PEER_IDX_MASK 0x000007ff
diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c
index b5305f0..c25d7dc 100644
--- a/drivers/nvmem/qfprom.c
+++ b/drivers/nvmem/qfprom.c
@@ -98,7 +98,26 @@
.of_match_table = qfprom_of_match,
},
};
-module_platform_driver(qfprom_driver);
+
+static int __init qfprom_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&qfprom_driver);
+ if (ret != 0)
+ pr_err("Failed to register qfprom driver\n");
+
+ return ret;
+}
+
+static void __exit qfprom_exit(void)
+{
+ return platform_driver_unregister(&qfprom_driver);
+}
+
+subsys_initcall(qfprom_init);
+module_exit(qfprom_exit);
+
MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>");
MODULE_DESCRIPTION("Qualcomm QFPROM driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index 69c8cb0..b897813 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -50,8 +50,7 @@
#include <linux/ipc_logging.h>
#include <linux/msm_pcie.h>
-#define PCIE_VENDOR_ID_RCP 0x17cb
-#define PCIE_DEVICE_ID_RCP 0x0106
+#define PCIE_VENDOR_ID_QCOM 0x17cb
#define PCIE20_L1SUB_CONTROL1 0x1E4
#define PCIE20_PARF_DBI_BASE_ADDR 0x350
@@ -63,7 +62,6 @@
#define PCIE_N_SW_RESET(n) (PCS_PORT(n) + 0x00)
#define PCIE_N_POWER_DOWN_CONTROL(n) (PCS_PORT(n) + 0x04)
-#define PCIE_N_PCS_STATUS(n) (PCS_PORT(n) + 0x174)
#define PCIE_GEN3_COM_INTEGLOOP_GAIN1_MODE0 0x0154
#define PCIE_GEN3_L0_DRVR_CTRL0 0x080c
@@ -71,7 +69,6 @@
#define PCIE_GEN3_L0_BIST_ERR_CNT1_STATUS 0x08a8
#define PCIE_GEN3_L0_BIST_ERR_CNT2_STATUS 0x08ac
#define PCIE_GEN3_L0_DEBUG_BUS_STATUS4 0x08bc
-#define PCIE_GEN3_PCIE_PHY_PCS_STATUS 0x1aac
#define PCIE20_PARF_SYS_CTRL 0x00
#define PCIE20_PARF_PM_CTRL 0x20
@@ -590,6 +587,7 @@
uint32_t switch_latency;
uint32_t wr_halt_size;
uint32_t slv_addr_space_size;
+ uint32_t phy_status_offset;
uint32_t cpl_timeout;
uint32_t current_bdf;
uint32_t perst_delay_us_min;
@@ -996,11 +994,7 @@
static bool pcie_phy_is_ready(struct msm_pcie_dev_t *dev)
{
- u32 pos = (dev->max_link_speed == GEN2_SPEED) ?
- PCIE_N_PCS_STATUS(dev->rc_idx) :
- PCIE_GEN3_PCIE_PHY_PCS_STATUS;
-
- if (readl_relaxed(dev->phy + pos) & BIT(6))
+ if (readl_relaxed(dev->phy + dev->phy_status_offset) & BIT(6))
return false;
else
return true;
@@ -1258,6 +1252,8 @@
dev->wr_halt_size);
PCIE_DBG_FS(dev, "slv_addr_space_size: 0x%x\n",
dev->slv_addr_space_size);
+ PCIE_DBG_FS(dev, "phy_status_offset: 0x%x\n",
+ dev->phy_status_offset);
PCIE_DBG_FS(dev, "cpl_timeout: 0x%x\n",
dev->cpl_timeout);
PCIE_DBG_FS(dev, "current_bdf: 0x%x\n",
@@ -5812,6 +5808,21 @@
"RC%d: slv-addr-space-size: 0x%x.\n",
rc_idx, msm_pcie_dev[rc_idx].slv_addr_space_size);
+ msm_pcie_dev[rc_idx].phy_status_offset = 0;
+ ret = of_property_read_u32(pdev->dev.of_node,
+ "qcom,phy-status-offset",
+ &msm_pcie_dev[rc_idx].phy_status_offset);
+ if (ret) {
+ PCIE_ERR(&msm_pcie_dev[rc_idx],
+ "RC%d: failed to get PCIe PHY status offset.\n",
+ rc_idx);
+ goto decrease_rc_num;
+ } else {
+ PCIE_DBG(&msm_pcie_dev[rc_idx],
+ "RC%d: phy-status-offset: 0x%x.\n",
+ rc_idx, msm_pcie_dev[rc_idx].phy_status_offset);
+ }
+
msm_pcie_dev[rc_idx].cpl_timeout = 0;
ret = of_property_read_u32((&pdev->dev)->of_node,
"qcom,cpl-timeout",
@@ -6208,10 +6219,10 @@
struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev->bus);
PCIE_DBG(pcie_dev, "hdr_type %d\n", dev->hdr_type);
- if (dev->hdr_type == 1)
+ if (pci_is_root_bus(dev->bus))
dev->class = (dev->class & 0xff) | (PCI_CLASS_BRIDGE_PCI << 8);
}
-DECLARE_PCI_FIXUP_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
+DECLARE_PCI_FIXUP_EARLY(PCIE_VENDOR_ID_QCOM, PCI_ANY_ID,
msm_pcie_fixup_early);
/* Suspend the PCIe link */
@@ -6294,7 +6305,8 @@
PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx);
- if (pcie_dev->link_status != MSM_PCIE_LINK_ENABLED)
+ if (pcie_dev->link_status != MSM_PCIE_LINK_ENABLED ||
+ !pci_is_root_bus(dev->bus))
return;
spin_lock_irqsave(&pcie_dev->cfg_lock,
@@ -6319,7 +6331,7 @@
mutex_unlock(&pcie_dev->recovery_lock);
}
-DECLARE_PCI_FIXUP_SUSPEND(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
+DECLARE_PCI_FIXUP_SUSPEND(PCIE_VENDOR_ID_QCOM, PCI_ANY_ID,
msm_pcie_fixup_suspend);
/* Resume the PCIe link */
@@ -6393,7 +6405,7 @@
PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx);
if ((pcie_dev->link_status != MSM_PCIE_LINK_DISABLED) ||
- pcie_dev->user_suspend)
+ pcie_dev->user_suspend || !pci_is_root_bus(dev->bus))
return;
mutex_lock(&pcie_dev->recovery_lock);
@@ -6405,7 +6417,7 @@
mutex_unlock(&pcie_dev->recovery_lock);
}
-DECLARE_PCI_FIXUP_RESUME(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
+DECLARE_PCI_FIXUP_RESUME(PCIE_VENDOR_ID_QCOM, PCI_ANY_ID,
msm_pcie_fixup_resume);
static void msm_pcie_fixup_resume_early(struct pci_dev *dev)
@@ -6416,7 +6428,7 @@
PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx);
if ((pcie_dev->link_status != MSM_PCIE_LINK_DISABLED) ||
- pcie_dev->user_suspend)
+ pcie_dev->user_suspend || !pci_is_root_bus(dev->bus))
return;
mutex_lock(&pcie_dev->recovery_lock);
@@ -6427,7 +6439,7 @@
mutex_unlock(&pcie_dev->recovery_lock);
}
-DECLARE_PCI_FIXUP_RESUME_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCIE_VENDOR_ID_QCOM, PCI_ANY_ID,
msm_pcie_fixup_resume_early);
int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user,
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
index 1335c45..a8946bf 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
@@ -72,6 +72,11 @@
IPA_USB_DRV_NAME " %s:%d " fmt, ## args); \
} while (0)
+enum ipa_usb_direction {
+ IPA_USB_DIR_UL,
+ IPA_USB_DIR_DL,
+};
+
struct ipa_usb_xdci_connect_params_internal {
enum ipa_usb_max_usb_packet_size max_pkt_size;
u32 ipa_to_usb_clnt_hdl;
@@ -167,7 +172,8 @@
int (*ipa_usb_notify_cb)(enum ipa_usb_notify_event, void *user_data);
void *user_data;
enum ipa3_usb_state state;
- struct ipa_usb_xdci_chan_params ch_params;
+ struct ipa_usb_xdci_chan_params ul_ch_params;
+ struct ipa_usb_xdci_chan_params dl_ch_params;
struct ipa3_usb_teth_prot_conn_params teth_conn_params;
};
@@ -1069,8 +1075,6 @@
params->gevntcount_hi_addr);
IPA_USB_DBG_LOW("dir = %d\n", params->dir);
IPA_USB_DBG_LOW("xfer_ring_len = %d\n", params->xfer_ring_len);
- IPA_USB_DBG_LOW("xfer_ring_base_addr = %llx\n",
- params->xfer_ring_base_addr);
IPA_USB_DBG_LOW("last_trb_addr_iova = %x\n",
params->xfer_scratch.last_trb_addr_iova);
IPA_USB_DBG_LOW("const_buffer_size = %d\n",
@@ -1174,15 +1178,16 @@
ipa3_usb_ctx->smmu_reg_map.cnt--;
}
+
result = ipa3_smmu_map_peer_buff(params->xfer_ring_base_addr_iova,
- params->xfer_ring_base_addr, params->xfer_ring_len, map);
+ params->xfer_ring_len, map, params->sgt_xfer_rings);
if (result) {
IPA_USB_ERR("failed to map Xfer ring %d\n", result);
return result;
}
result = ipa3_smmu_map_peer_buff(params->data_buff_base_addr_iova,
- params->data_buff_base_addr, params->data_buff_base_len, map);
+ params->data_buff_base_len, map, params->sgt_data_buff);
if (result) {
IPA_USB_ERR("failed to map TRBs buff %d\n", result);
return result;
@@ -1191,13 +1196,52 @@
return 0;
}
+static int ipa3_usb_smmu_store_sgt(struct sg_table **out_ch_ptr,
+ struct sg_table *in_sgt_ptr)
+{
+ unsigned int nents;
+
+ if (in_sgt_ptr != NULL) {
+ *out_ch_ptr = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (*out_ch_ptr == NULL)
+ return -ENOMEM;
+
+ nents = in_sgt_ptr->nents;
+
+ (*out_ch_ptr)->sgl =
+ kcalloc(nents, sizeof(struct scatterlist),
+ GFP_KERNEL);
+ if ((*out_ch_ptr)->sgl == NULL)
+ return -ENOMEM;
+
+ memcpy((*out_ch_ptr)->sgl, in_sgt_ptr->sgl,
+ nents*sizeof((*out_ch_ptr)->sgl));
+ (*out_ch_ptr)->nents = nents;
+ (*out_ch_ptr)->orig_nents = in_sgt_ptr->orig_nents;
+ }
+ return 0;
+}
+
+static int ipa3_usb_smmu_free_sgt(struct sg_table **out_sgt_ptr)
+{
+ if (*out_sgt_ptr != NULL) {
+ kfree((*out_sgt_ptr)->sgl);
+ (*out_sgt_ptr)->sgl = NULL;
+ kfree(*out_sgt_ptr);
+ *out_sgt_ptr = NULL;
+ }
+ return 0;
+}
+
static int ipa3_usb_request_xdci_channel(
struct ipa_usb_xdci_chan_params *params,
+ enum ipa_usb_direction dir,
struct ipa_req_chan_out_params *out_params)
{
int result = -EFAULT;
struct ipa_request_gsi_channel_params chan_params;
enum ipa3_usb_transport_type ttype;
+ struct ipa_usb_xdci_chan_params *xdci_ch_params;
IPA_USB_DBG_LOW("entry\n");
if (params == NULL || out_params == NULL ||
@@ -1273,8 +1317,26 @@
}
/* store channel params for SMMU unmap */
- ipa3_usb_ctx->ttype_ctx[ttype].ch_params = *params;
+ if (dir == IPA_USB_DIR_UL)
+ xdci_ch_params = &ipa3_usb_ctx->ttype_ctx[ttype].ul_ch_params;
+ else
+ xdci_ch_params = &ipa3_usb_ctx->ttype_ctx[ttype].dl_ch_params;
+ *xdci_ch_params = *params;
+ result = ipa3_usb_smmu_store_sgt(
+ &xdci_ch_params->sgt_xfer_rings,
+ params->sgt_xfer_rings);
+ if (result) {
+ ipa3_usb_smmu_free_sgt(&xdci_ch_params->sgt_xfer_rings);
+ return result;
+ }
+ result = ipa3_usb_smmu_store_sgt(
+ &xdci_ch_params->sgt_data_buff,
+ params->sgt_data_buff);
+ if (result) {
+ ipa3_usb_smmu_free_sgt(&xdci_ch_params->sgt_data_buff);
+ return result;
+ }
chan_params.keep_ipa_awake = params->keep_ipa_awake;
chan_params.evt_ring_params.intf = GSI_EVT_CHTYPE_XDCI_EV;
chan_params.evt_ring_params.intr = GSI_INTR_IRQ;
@@ -1282,7 +1344,7 @@
chan_params.evt_ring_params.ring_len = params->xfer_ring_len -
chan_params.evt_ring_params.re_size;
chan_params.evt_ring_params.ring_base_addr =
- params->xfer_ring_base_addr;
+ params->xfer_ring_base_addr_iova;
chan_params.evt_ring_params.ring_base_vaddr = NULL;
chan_params.evt_ring_params.int_modt = 0;
chan_params.evt_ring_params.int_modt = 0;
@@ -1302,7 +1364,7 @@
chan_params.chan_params.re_size = GSI_CHAN_RE_SIZE_16B;
chan_params.chan_params.ring_len = params->xfer_ring_len;
chan_params.chan_params.ring_base_addr =
- params->xfer_ring_base_addr;
+ params->xfer_ring_base_addr_iova;
chan_params.chan_params.ring_base_vaddr = NULL;
chan_params.chan_params.use_db_eng = GSI_CHAN_DB_MODE;
chan_params.chan_params.max_prefetch = GSI_ONE_PREFETCH_SEG;
@@ -1345,9 +1407,11 @@
}
static int ipa3_usb_release_xdci_channel(u32 clnt_hdl,
+ enum ipa_usb_direction dir,
enum ipa3_usb_transport_type ttype)
{
int result = 0;
+ struct ipa_usb_xdci_chan_params *xdci_ch_params;
IPA_USB_DBG_LOW("entry\n");
if (ttype < 0 || ttype >= IPA_USB_TRANSPORT_MAX) {
@@ -1367,8 +1431,17 @@
return result;
}
- result = ipa3_usb_smmu_map_xdci_channel(
- &ipa3_usb_ctx->ttype_ctx[ttype].ch_params, false);
+ if (dir == IPA_USB_DIR_UL)
+ xdci_ch_params = &ipa3_usb_ctx->ttype_ctx[ttype].ul_ch_params;
+ else
+ xdci_ch_params = &ipa3_usb_ctx->ttype_ctx[ttype].dl_ch_params;
+
+ result = ipa3_usb_smmu_map_xdci_channel(xdci_ch_params, false);
+
+ if (xdci_ch_params->sgt_xfer_rings != NULL)
+ ipa3_usb_smmu_free_sgt(&xdci_ch_params->sgt_xfer_rings);
+ if (xdci_ch_params->sgt_data_buff != NULL)
+ ipa3_usb_smmu_free_sgt(&xdci_ch_params->sgt_data_buff);
/* Change ipa_usb state to INITIALIZED */
if (!ipa3_usb_set_state(IPA_USB_INITIALIZED, false, ttype))
@@ -2131,14 +2204,15 @@
if (connect_params->teth_prot != IPA_USB_DIAG) {
result = ipa3_usb_request_xdci_channel(ul_chan_params,
- ul_out_params);
+ IPA_USB_DIR_UL, ul_out_params);
if (result) {
IPA_USB_ERR("failed to allocate UL channel.\n");
goto bad_params;
}
}
- result = ipa3_usb_request_xdci_channel(dl_chan_params, dl_out_params);
+ result = ipa3_usb_request_xdci_channel(dl_chan_params, IPA_USB_DIR_DL,
+ dl_out_params);
if (result) {
IPA_USB_ERR("failed to allocate DL/DPL channel.\n");
goto alloc_dl_chan_fail;
@@ -2174,11 +2248,12 @@
return 0;
connect_fail:
- ipa3_usb_release_xdci_channel(dl_out_params->clnt_hdl,
+ ipa3_usb_release_xdci_channel(dl_out_params->clnt_hdl, IPA_USB_DIR_DL,
IPA3_USB_GET_TTYPE(dl_chan_params->teth_prot));
alloc_dl_chan_fail:
if (connect_params->teth_prot != IPA_USB_DIAG)
ipa3_usb_release_xdci_channel(ul_out_params->clnt_hdl,
+ IPA_USB_DIR_UL,
IPA3_USB_GET_TTYPE(ul_chan_params->teth_prot));
bad_params:
mutex_unlock(&ipa3_usb_ctx->general_mutex);
@@ -2250,14 +2325,16 @@
IPA_USB_ERR("failed to change state to stopped\n");
if (!IPA3_USB_IS_TTYPE_DPL(ttype)) {
- result = ipa3_usb_release_xdci_channel(ul_clnt_hdl, ttype);
+ result = ipa3_usb_release_xdci_channel(ul_clnt_hdl,
+ IPA_USB_DIR_UL, ttype);
if (result) {
IPA_USB_ERR("failed to release UL channel.\n");
return result;
}
}
- result = ipa3_usb_release_xdci_channel(dl_clnt_hdl, ttype);
+ result = ipa3_usb_release_xdci_channel(dl_clnt_hdl,
+ IPA_USB_DIR_DL, ttype);
if (result) {
IPA_USB_ERR("failed to release DL channel.\n");
return result;
@@ -2869,9 +2946,8 @@
pr_debug("entry\n");
ipa3_usb_ctx = kzalloc(sizeof(struct ipa3_usb_context), GFP_KERNEL);
if (ipa3_usb_ctx == NULL) {
- pr_err("failed to allocate memory\n");
pr_err(":ipa_usb init failed\n");
- return -EFAULT;
+ return -ENOMEM;
}
memset(ipa3_usb_ctx, 0, sizeof(struct ipa3_usb_context));
diff --git a/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c b/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c
index 613bed3..2723a35 100644
--- a/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c
+++ b/drivers/platform/msm/ipa/ipa_rm_inactivity_timer.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2018, 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
@@ -140,7 +140,7 @@
ipa_rm_it_handles[resource_name].work_in_progress = false;
pwlock = &(ipa_rm_it_handles[resource_name].w_lock);
name = ipa_rm_it_handles[resource_name].w_lock_name;
- snprintf(name, MAX_WS_NAME, "IPA_RM%d\n", resource_name);
+ snprintf(name, MAX_WS_NAME, "IPA_RM%d", resource_name);
wakeup_source_init(pwlock, name);
INIT_DELAYED_WORK(&ipa_rm_it_handles[resource_name].work,
ipa_rm_inactivity_timer_func);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
index 37614cc..f062ed2 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, 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
@@ -1455,7 +1455,11 @@
pr_err("Table Size:%d\n",
ipa_ctx->nat_mem.size_base_tables);
- pr_err("Expansion Table Size:%d\n",
+ if (!ipa_ctx->nat_mem.size_expansion_tables)
+ pr_err("Expansion Table Size:%d\n",
+ ipa_ctx->nat_mem.size_expansion_tables);
+ else
+ pr_err("Expansion Table Size:%d\n",
ipa_ctx->nat_mem.size_expansion_tables-1);
if (!ipa_ctx->nat_mem.is_sys_mem)
@@ -1470,6 +1474,8 @@
pr_err("\nBase Table:\n");
} else {
+ if (!ipa_ctx->nat_mem.size_expansion_tables)
+ continue;
tbl_size = ipa_ctx->nat_mem.size_expansion_tables-1;
base_tbl =
(u32 *)ipa_ctx->nat_mem.ipv4_expansion_rules_addr;
@@ -1569,6 +1575,8 @@
pr_err("\nIndex Table:\n");
} else {
+ if (!ipa_ctx->nat_mem.size_expansion_tables)
+ continue;
tbl_size = ipa_ctx->nat_mem.size_expansion_tables-1;
indx_tbl =
(u32 *)ipa_ctx->nat_mem.index_table_expansion_addr;
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index 5b2d13a..068c6c5 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -1661,6 +1661,7 @@
IPAWANERR("Failed to allocate memory.\n");
return -ENOMEM;
}
+ extend_ioctl_data.u.if_name[IFNAMSIZ-1] = '\0';
len = sizeof(wan_msg->upstream_ifname) >
sizeof(extend_ioctl_data.u.if_name) ?
sizeof(extend_ioctl_data.u.if_name) :
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index 901b51b..17e4838 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
@@ -548,10 +548,17 @@
return 0;
}
-int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr, u32 size, bool map)
+int ipa3_smmu_map_peer_buff(u64 iova, u32 size, bool map, struct sg_table *sgt)
{
struct iommu_domain *smmu_domain;
int res;
+ phys_addr_t phys;
+ unsigned long va;
+ struct scatterlist *sg;
+ int count = 0;
+ size_t len;
+ int i;
+ struct page *page;
if (ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_AP])
return 0;
@@ -562,33 +569,53 @@
return -EINVAL;
}
+ /*
+ * USB GSI driver would update sgt irrespective of USB S1
+ * is enable or bypass.
+ * If USB S1 is enabled using IOMMU, iova != pa.
+ * If USB S1 is bypass, iova == pa.
+ */
if (map) {
- res = ipa3_iommu_map(smmu_domain,
- rounddown(iova, PAGE_SIZE),
- rounddown(phys_addr, PAGE_SIZE),
- roundup(size + iova - rounddown(iova, PAGE_SIZE),
- PAGE_SIZE),
- IOMMU_READ | IOMMU_WRITE);
- if (res) {
- IPAERR("Fail to map 0x%llx->0x%pa\n", iova, &phys_addr);
- return -EINVAL;
+ if (sgt != NULL) {
+ va = rounddown(iova, PAGE_SIZE);
+ for_each_sg(sgt->sgl, sg, sgt->nents, i) {
+ page = sg_page(sg);
+ phys = page_to_phys(page);
+ len = PAGE_ALIGN(sg->offset + sg->length);
+ res = ipa3_iommu_map(smmu_domain, va, phys,
+ len, IOMMU_READ | IOMMU_WRITE);
+ if (res) {
+ IPAERR("Fail to map pa=%pa\n", &phys);
+ return -EINVAL;
+ }
+ va += len;
+ count++;
+ }
+ } else {
+ res = ipa3_iommu_map(smmu_domain,
+ rounddown(iova, PAGE_SIZE),
+ rounddown(iova, PAGE_SIZE),
+ roundup(size + iova -
+ rounddown(iova, PAGE_SIZE),
+ PAGE_SIZE),
+ IOMMU_READ | IOMMU_WRITE);
+ if (res) {
+ IPAERR("Fail to map 0x%llx\n", iova);
+ return -EINVAL;
+ }
}
} else {
res = iommu_unmap(smmu_domain,
- rounddown(iova, PAGE_SIZE),
- roundup(size + iova - rounddown(iova, PAGE_SIZE),
- PAGE_SIZE));
+ rounddown(iova, PAGE_SIZE),
+ roundup(size + iova - rounddown(iova, PAGE_SIZE),
+ PAGE_SIZE));
if (res != roundup(size + iova - rounddown(iova, PAGE_SIZE),
PAGE_SIZE)) {
- IPAERR("Fail to unmap 0x%llx->0x%pa\n",
- iova, &phys_addr);
+ IPAERR("Fail to unmap 0x%llx\n", iova);
return -EINVAL;
}
}
-
- IPADBG("Peer buff %s 0x%llx->0x%pa\n", map ? "map" : "unmap",
- iova, &phys_addr);
-
+ IPADBG("Peer buff %s 0x%llx\n", map ? "map" : "unmap", iova);
return 0;
}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index c0fe730..90edd2b 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -222,7 +222,13 @@
tx_pkt->no_unmap_dma = true;
tx_pkt->sys = sys;
spin_lock_bh(&sys->spinlock);
+ if (unlikely(!sys->nop_pending)) {
+ spin_unlock_bh(&sys->spinlock);
+ kmem_cache_free(ipa3_ctx->tx_pkt_wrapper_cache, tx_pkt);
+ return;
+ }
list_add_tail(&tx_pkt->link, &sys->head_desc_list);
+ sys->nop_pending = false;
spin_unlock_bh(&sys->spinlock);
memset(&nop_xfer, 0, sizeof(nop_xfer));
@@ -273,6 +279,7 @@
int result;
u32 mem_flag = GFP_ATOMIC;
const struct ipa_gsi_ep_config *gsi_ep_cfg;
+ bool send_nop = false;
if (unlikely(!in_atomic))
mem_flag = GFP_KERNEL;
@@ -410,10 +417,14 @@
}
kfree(gsi_xfer_elem_array);
+ if (sys->use_comm_evt_ring && !sys->nop_pending) {
+ sys->nop_pending = true;
+ send_nop = true;
+ }
spin_unlock_bh(&sys->spinlock);
/* set the timer for sending the NOP descriptor */
- if (sys->use_comm_evt_ring && !hrtimer_active(&sys->db_timer)) {
+ if (send_nop) {
ktime_t time = ktime_set(0, IPA_TX_SEND_COMPL_NOP_DELAY_NS);
IPADBG_LOW("scheduling timer for ch %lu\n",
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index d7d74a3..7bd1731 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, 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
@@ -626,6 +626,7 @@
struct delayed_work switch_to_intr_work;
enum ipa3_sys_pipe_policy policy;
bool use_comm_evt_ring;
+ bool nop_pending;
int (*pyld_hdlr)(struct sk_buff *skb, struct ipa3_sys_context *sys);
struct sk_buff * (*get_skb)(unsigned int len, gfp_t flags);
void (*free_skb)(struct sk_buff *skb);
@@ -2300,8 +2301,7 @@
int ipa3_rx_poll(u32 clnt_hdl, int budget);
void ipa3_recycle_wan_skb(struct sk_buff *skb);
int ipa3_smmu_map_peer_reg(phys_addr_t phys_addr, bool map);
-int ipa3_smmu_map_peer_buff(u64 iova, phys_addr_t phys_addr,
- u32 size, bool map);
+int ipa3_smmu_map_peer_buff(u64 iova, u32 size, bool map, struct sg_table *sgt);
void ipa3_reset_freeze_vote(void);
int ipa3_ntn_init(void);
int ipa3_get_ntn_stats(struct Ipa3HwStatsNTNInfoData_t *stats);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index 1782fdc..9974b87 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -1117,7 +1117,7 @@
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 7, 0, 8, 16, IPA_EE_UC } },
+ { 6, 2, 8, 16, IPA_EE_UC } },
[IPA_4_0][IPA_CLIENT_USB_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
@@ -1153,13 +1153,7 @@
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 9, 1, 8, 16, IPA_EE_UC } },
- [IPA_4_0][IPA_CLIENT_Q6_LAN_PROD] = {
- true, IPA_v4_0_GROUP_UL_DL,
- true,
- IPA_DPS_HPS_SEQ_TYPE_PKT_PROCESS_NO_DEC_UCP,
- QMB_MASTER_SELECT_DDR,
- { 6, 2, 12, 24, IPA_EE_Q6 } },
+ { 9, 0, 8, 16, IPA_EE_UC } },
[IPA_4_0][IPA_CLIENT_Q6_WAN_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
@@ -1196,13 +1190,13 @@
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- {7, 0, 8, 16, IPA_EE_UC } },
+ { 7, 9, 8, 16, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST4_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_DDR,
- { 8, 10, 8, 16, IPA_EE_AP } },
+ {8, 10, 8, 16, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_WLAN1_CONS] = {
@@ -1210,7 +1204,7 @@
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 18, 2, 6, 9, IPA_EE_UC } },
+ { 18, 3, 6, 9, IPA_EE_UC } },
[IPA_4_0][IPA_CLIENT_WLAN2_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
@@ -1258,7 +1252,7 @@
true,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
- { 22, 3, 17, 17, IPA_EE_UC } },
+ { 22, 1, 17, 17, IPA_EE_UC } },
[IPA_4_0][IPA_CLIENT_Q6_LAN_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
@@ -1284,25 +1278,25 @@
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_PCIE,
- { 12, 2, 5, 5, IPA_EE_AP } },
+ { 11, 6, 9, 9, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST1_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { 12, 2, 5, 5, IPA_EE_AP } },
+ QMB_MASTER_SELECT_PCIE,
+ { 11, 6, 9, 9, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST2_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_PCIE,
- { 18, 2, 6, 9, IPA_EE_UC } },
+ { 12, 2, 5, 5, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST3_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { 20, 13, 9, 9, IPA_EE_AP } },
+ QMB_MASTER_SELECT_PCIE,
+ { 19, 12, 9, 9, IPA_EE_AP } },
[IPA_4_0][IPA_CLIENT_TEST4_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
@@ -1318,12 +1312,6 @@
{ 31, 31, 8, 8, IPA_EE_AP } },
/* IPA_4_0_MHI */
- [IPA_4_0_MHI][IPA_CLIENT_USB_PROD] = {
- false, IPA_EP_NOT_ALLOCATED,
- true,
- IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { -1, -1, -1, -1, -1 } },
[IPA_4_0_MHI][IPA_CLIENT_APPS_WAN_PROD] = {
true, IPA_v4_0_MHI_GROUP_DDR,
true,
@@ -1342,12 +1330,6 @@
IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
QMB_MASTER_SELECT_PCIE,
{ 1, 0, 8, 16, IPA_EE_AP } },
- [IPA_4_0_MHI][IPA_CLIENT_Q6_LAN_PROD] = {
- true, IPA_v4_0_MHI_GROUP_DDR,
- true,
- IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
- QMB_MASTER_SELECT_DDR,
- { 6, 2, 12, 24, IPA_EE_Q6 } },
[IPA_4_0_MHI][IPA_CLIENT_Q6_WAN_PROD] = {
true, IPA_v4_0_GROUP_UL_DL,
true,
@@ -1404,18 +1386,7 @@
QMB_MASTER_SELECT_DDR,
{ 8, 10, 8, 16, IPA_EE_AP } },
- [IPA_4_0_MHI][IPA_CLIENT_USB_CONS] = {
- false, IPA_EP_NOT_ALLOCATED,
- false,
- IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { -1, -1, -1, -1, -1 } },
- [IPA_4_0_MHI][IPA_CLIENT_USB_DPL_CONS] = {
- false, IPA_EP_NOT_ALLOCATED,
- false,
- IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { -1, -1, -1, -1, -1 } },
+
[IPA_4_0_MHI][IPA_CLIENT_APPS_LAN_CONS] = {
true, IPA_v4_0_MHI_GROUP_DDR,
false,
@@ -1470,25 +1441,25 @@
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_PCIE,
- { 12, 2, 5, 5, IPA_EE_AP } },
+ { 11, 6, 9, 9, IPA_EE_AP } },
[IPA_4_0_MHI][IPA_CLIENT_TEST1_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { 12, 2, 5, 5, IPA_EE_AP } },
+ QMB_MASTER_SELECT_PCIE,
+ { 11, 6, 9, 9, IPA_EE_AP } },
[IPA_4_0_MHI][IPA_CLIENT_TEST2_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_PCIE,
- { 18, 11, 6, 9, IPA_EE_AP } },
+ QMB_MASTER_SELECT_DDR,
+ { 12, 2, 5, 5, IPA_EE_AP } },
[IPA_4_0_MHI][IPA_CLIENT_TEST3_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
- QMB_MASTER_SELECT_DDR,
- { 20, 13, 9, 9, IPA_EE_AP } },
+ QMB_MASTER_SELECT_PCIE,
+ { 19, 12, 9, 9, IPA_EE_AP } },
[IPA_4_0_MHI][IPA_CLIENT_TEST4_CONS] = {
true, IPA_v4_0_GROUP_UL_DL,
false,
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
index 4920942..48e7d7c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
@@ -2129,15 +2129,13 @@
return;
}
- valmask->val = (1 & IPA_ENDP_INIT_AGGR_n_AGGR_FORCE_CLOSE_BMSK) <<
- IPA_ENDP_INIT_AGGR_n_AGGR_FORCE_CLOSE_SHFT;
- valmask->mask = IPA_ENDP_INIT_AGGR_n_AGGR_FORCE_CLOSE_BMSK <<
- IPA_ENDP_INIT_AGGR_n_AGGR_FORCE_CLOSE_SHFT;
+ valmask->val = (1 << IPA_ENDP_INIT_AGGR_n_AGGR_FORCE_CLOSE_SHFT) &&
+ IPA_ENDP_INIT_AGGR_n_AGGR_FORCE_CLOSE_BMSK;
+ valmask->mask = IPA_ENDP_INIT_AGGR_n_AGGR_FORCE_CLOSE_BMSK;
- valmask->val |= ((0 & IPA_ENDP_INIT_AGGR_n_AGGR_EN_BMSK) <<
- IPA_ENDP_INIT_AGGR_n_AGGR_EN_SHFT);
- valmask->mask |= ((IPA_ENDP_INIT_AGGR_n_AGGR_EN_BMSK <<
- IPA_ENDP_INIT_AGGR_n_AGGR_EN_SHFT));
+ valmask->val |= ((0 << IPA_ENDP_INIT_AGGR_n_AGGR_EN_SHFT) &&
+ IPA_ENDP_INIT_AGGR_n_AGGR_EN_BMSK);
+ valmask->mask |= IPA_ENDP_INIT_AGGR_n_AGGR_EN_BMSK;
}
u32 ipahal_aggr_get_max_byte_limit(void)
@@ -2188,7 +2186,7 @@
return;
}
IPA_SETFIELD_IN_REG(valmask->val, 1 << ep_idx, shft, bmsk);
- valmask->mask = bmsk << shft;
+ valmask->mask = bmsk;
}
void ipahal_get_fltrt_hash_flush_valmask(
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index 8285ae7..512dddd 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -1740,6 +1740,7 @@
IPAWANERR("Failed to allocate memory.\n");
return -ENOMEM;
}
+ extend_ioctl_data.u.if_name[IFNAMSIZ-1] = '\0';
len = sizeof(wan_msg->upstream_ifname) >
sizeof(extend_ioctl_data.u.if_name) ?
sizeof(extend_ioctl_data.u.if_name) :
@@ -4025,6 +4026,10 @@
ipa3_qmi_init();
/* Register for Modem SSR */
+ /* SSR is not supported yet on IPA 4.0 */
+ if (ipa3_ctx->ipa_hw_type == IPA_HW_v4_0)
+ return platform_driver_register(&rmnet_ipa_driver);
+
rmnet_ipa3_ctx->subsys_notify_handle = subsys_notif_register_notifier(
SUBSYS_MODEM,
&ipa3_ssr_notifier);
diff --git a/drivers/platform/msm/ipa/test/ipa_ut_framework.c b/drivers/platform/msm/ipa/test/ipa_ut_framework.c
index 85699bb..bcbcd87 100644
--- a/drivers/platform/msm/ipa/test/ipa_ut_framework.c
+++ b/drivers/platform/msm/ipa/test/ipa_ut_framework.c
@@ -971,6 +971,7 @@
IS_ERR(ipa_ut_ctx->test_dbgfs_root)) {
IPA_UT_ERR("failed to create test debugfs dir\n");
ret = -EFAULT;
+ destroy_workqueue(ipa_ut_ctx->wq);
goto unlock_mutex;
}
@@ -980,6 +981,7 @@
if (!dfile_enable || IS_ERR(dfile_enable)) {
IPA_UT_ERR("failed to create enable debugfs file\n");
ret = -EFAULT;
+ destroy_workqueue(ipa_ut_ctx->wq);
goto fail_clean_dbgfs;
}
diff --git a/drivers/platform/msm/msm_ext_display.c b/drivers/platform/msm/msm_ext_display.c
index bc4df04..7a93e0e 100644
--- a/drivers/platform/msm/msm_ext_display.c
+++ b/drivers/platform/msm/msm_ext_display.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -44,6 +44,20 @@
EXTCON_NONE,
};
+static int msm_ext_disp_find_index(struct extcon_dev *edev,
+ enum msm_ext_disp_type id)
+{
+ int i;
+
+ /* Find the the index of extcon cable in edev->supported_cable */
+ for (i = 0; i < edev->max_supported; i++) {
+ if (edev->supported_cable[i] == id)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
static int msm_ext_disp_extcon_register(struct msm_ext_disp *ext_disp)
{
int ret = 0;
@@ -145,7 +159,8 @@
enum msm_ext_disp_cable_state new_state)
{
int ret = 0;
- int state;
+ int state, index;
+ enum msm_ext_disp_cable_state current_state;
if (!ext_disp->ops) {
pr_err("codec not registered, skip notification\n");
@@ -154,13 +169,27 @@
}
state = ext_disp->audio_sdev.state;
- ret = extcon_set_state_sync(&ext_disp->audio_sdev,
- ext_disp->current_disp, !!new_state);
- pr_debug("Audio state %s %d\n",
- ext_disp->audio_sdev.state == state ?
- "is same" : "switched to",
- ext_disp->audio_sdev.state);
+ index = msm_ext_disp_find_index(&ext_disp->audio_sdev, type);
+ if (index < 0 || index >= ext_disp->audio_sdev.max_supported) {
+ pr_err("invalid index\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ if (state & BIT(index))
+ current_state = EXT_DISPLAY_CABLE_CONNECT;
+ else
+ current_state = EXT_DISPLAY_CABLE_DISCONNECT;
+
+ if (current_state == new_state) {
+ ret = -EEXIST;
+ pr_debug("same state\n");
+ } else {
+ ret = extcon_set_state_sync(&ext_disp->audio_sdev,
+ ext_disp->current_disp, !!new_state);
+ pr_debug("state changed to %d\n", new_state);
+ }
end:
return ret;
}
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index ceeb8c1..00d82e8 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -848,5 +848,5 @@
pr_info("Mapper unloaded\n");
}
-subsys_initcall(acpi_wmi_init);
+subsys_initcall_sync(acpi_wmi_init);
module_exit(acpi_wmi_exit);
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index e7d13ae..8e367c5 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -350,6 +350,16 @@
To compile this driver as a module, choose M here: the module
will be called pwm-rcar.
+config PWM_QTI_LPG
+ tristate "Qualcomm Technologies, Inc. LPG driver"
+ depends on MFD_SPMI_PMIC && OF
+ help
+ This driver supports the LPG (Light Pulse Generator) module found in
+ Qualcomm Technologies, Inc. PMIC chips. Each LPG channel can be
+ configured to operate in PWM mode to output a fixed amplitude with
+ variable duty cycle or in LUT (Look up table) mode to output PWM
+ signal with a modulated amplitude.
+
config PWM_RENESAS_TPU
tristate "Renesas TPU PWM support"
depends on ARCH_RENESAS || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 24c1baf..9453eb0 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -33,6 +33,7 @@
obj-$(CONFIG_PWM_PXA) += pwm-pxa.o
obj-$(CONFIG_PWM_QPNP) += pwm-qpnp.o
obj-$(CONFIG_PWM_RCAR) += pwm-rcar.o
+obj-$(CONFIG_PWM_QTI_LPG) += pwm-qti-lpg.o
obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o
obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
diff --git a/drivers/pwm/pwm-qti-lpg.c b/drivers/pwm/pwm-qti-lpg.c
new file mode 100644
index 0000000..328f4b6
--- /dev/null
+++ b/drivers/pwm/pwm-qti-lpg.c
@@ -0,0 +1,570 @@
+/* Copyright (c) 2018, 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#define REG_SIZE_PER_LPG 0x100
+
+#define REG_LPG_PWM_SIZE_CLK 0x41
+#define REG_LPG_PWM_FREQ_PREDIV_CLK 0x42
+#define REG_LPG_PWM_TYPE_CONFIG 0x43
+#define REG_LPG_PWM_VALUE_LSB 0x44
+#define REG_LPG_PWM_VALUE_MSB 0x45
+#define REG_LPG_ENABLE_CONTROL 0x46
+#define REG_LPG_PWM_SYNC 0x47
+
+/* REG_LPG_PWM_SIZE_CLK */
+#define LPG_PWM_SIZE_MASK BIT(4)
+#define LPG_PWM_SIZE_SHIFT 4
+#define LPG_PWM_CLK_FREQ_SEL_MASK GENMASK(1, 0)
+
+/* REG_LPG_PWM_FREQ_PREDIV_CLK */
+#define LPG_PWM_FREQ_PREDIV_MASK GENMASK(6, 5)
+#define LPG_PWM_FREQ_PREDIV_SHIFT 5
+#define LPG_PWM_FREQ_EXPONENT_MASK GENMASK(2, 0)
+
+/* REG_LPG_PWM_TYPE_CONFIG */
+#define LPG_PWM_EN_GLITCH_REMOVAL_MASK BIT(5)
+
+/* REG_LPG_PWM_VALUE_LSB */
+#define LPG_PWM_VALUE_LSB_MASK GENMASK(7, 0)
+
+/* REG_LPG_PWM_VALUE_MSB */
+#define LPG_PWM_VALUE_MSB_MASK BIT(0)
+
+/* REG_LPG_ENABLE_CONTROL */
+#define LPG_EN_LPG_OUT_BIT BIT(7)
+#define LPG_PWM_SRC_SELECT_MASK BIT(2)
+#define LPG_PWM_SRC_SELECT_SHIFT 2
+#define LPG_EN_RAMP_GEN_MASK BIT(1)
+#define LPG_EN_RAMP_GEN_SHIFT 1
+
+/* REG_LPG_PWM_SYNC */
+#define LPG_PWM_VALUE_SYNC BIT(0)
+
+#define NUM_PWM_SIZE 2
+#define NUM_PWM_CLK 3
+#define NUM_CLK_PREDIV 4
+#define NUM_PWM_EXP 8
+
+enum {
+ LUT_PATTERN = 0,
+ PWM_OUTPUT,
+};
+
+static const int pwm_size[NUM_PWM_SIZE] = {6, 9};
+static const int clk_freq_hz[NUM_PWM_CLK] = {1024, 32768, 19200000};
+static const int clk_prediv[NUM_CLK_PREDIV] = {1, 3, 5, 6};
+static const int pwm_exponent[NUM_PWM_EXP] = {0, 1, 2, 3, 4, 5, 6, 7};
+
+struct lpg_pwm_config {
+ u32 pwm_size;
+ u32 pwm_clk;
+ u32 prediv;
+ u32 clk_exp;
+ u16 pwm_value;
+ u32 best_period_ns;
+};
+
+struct qpnp_lpg_channel {
+ struct qpnp_lpg_chip *chip;
+ struct lpg_pwm_config pwm_config;
+ u32 lpg_idx;
+ u32 reg_base;
+ u8 src_sel;
+ int current_period_ns;
+ int current_duty_ns;
+};
+
+struct qpnp_lpg_chip {
+ struct pwm_chip pwm_chip;
+ struct regmap *regmap;
+ struct device *dev;
+ struct qpnp_lpg_channel *lpgs;
+ struct mutex bus_lock;
+ u32 num_lpgs;
+};
+
+static int qpnp_lpg_write(struct qpnp_lpg_channel *lpg, u16 addr, u8 val)
+{
+ int rc;
+
+ mutex_lock(&lpg->chip->bus_lock);
+ rc = regmap_write(lpg->chip->regmap, lpg->reg_base + addr, val);
+ if (rc < 0)
+ dev_err(lpg->chip->dev, "Write addr 0x%x with value %d failed, rc=%d\n",
+ lpg->reg_base + addr, val, rc);
+ mutex_unlock(&lpg->chip->bus_lock);
+
+ return rc;
+}
+
+static int qpnp_lpg_masked_write(struct qpnp_lpg_channel *lpg,
+ u16 addr, u8 mask, u8 val)
+{
+ int rc;
+
+ mutex_lock(&lpg->chip->bus_lock);
+ rc = regmap_update_bits(lpg->chip->regmap, lpg->reg_base + addr,
+ mask, val);
+ if (rc < 0)
+ dev_err(lpg->chip->dev, "Update addr 0x%x to val 0x%x with mask 0x%x failed, rc=%d\n",
+ lpg->reg_base + addr, val, mask, rc);
+ mutex_unlock(&lpg->chip->bus_lock);
+
+ return rc;
+}
+
+static struct qpnp_lpg_channel *pwm_dev_to_qpnp_lpg(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm) {
+
+ struct qpnp_lpg_chip *chip = container_of(pwm_chip,
+ struct qpnp_lpg_chip, pwm_chip);
+ u32 hw_idx = pwm->hwpwm;
+
+ if (hw_idx >= chip->num_lpgs) {
+ dev_err(chip->dev, "hw index %d out of range [0-%d]\n",
+ hw_idx, chip->num_lpgs - 1);
+ return NULL;
+ }
+
+ return &chip->lpgs[hw_idx];
+}
+
+static int __find_index_in_array(int member, const int array[], int length)
+{
+ int i;
+
+ for (i = 0; i < length; i++) {
+ if (member == array[i])
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int qpnp_lpg_set_pwm_config(struct qpnp_lpg_channel *lpg)
+{
+ int rc;
+ u8 val, mask;
+ int pwm_size_idx, pwm_clk_idx, prediv_idx, clk_exp_idx;
+
+ pwm_size_idx = __find_index_in_array(lpg->pwm_config.pwm_size,
+ pwm_size, ARRAY_SIZE(pwm_size));
+ pwm_clk_idx = __find_index_in_array(lpg->pwm_config.pwm_clk,
+ clk_freq_hz, ARRAY_SIZE(clk_freq_hz));
+ prediv_idx = __find_index_in_array(lpg->pwm_config.prediv,
+ clk_prediv, ARRAY_SIZE(clk_prediv));
+ clk_exp_idx = __find_index_in_array(lpg->pwm_config.clk_exp,
+ pwm_exponent, ARRAY_SIZE(pwm_exponent));
+
+ if (pwm_size_idx < 0 || pwm_clk_idx < 0
+ || prediv_idx < 0 || clk_exp_idx < 0)
+ return -EINVAL;
+
+ /* pwm_clk_idx is 1 bit lower than the register value */
+ pwm_clk_idx += 1;
+ val = pwm_size_idx << LPG_PWM_SIZE_SHIFT | pwm_clk_idx;
+ mask = LPG_PWM_SIZE_MASK | LPG_PWM_CLK_FREQ_SEL_MASK;
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_PWM_SIZE_CLK, mask, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_SIZE_CLK failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = prediv_idx << LPG_PWM_FREQ_PREDIV_SHIFT | clk_exp_idx;
+ mask = LPG_PWM_FREQ_PREDIV_MASK | LPG_PWM_FREQ_EXPONENT_MASK;
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_PWM_FREQ_PREDIV_CLK, mask, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_FREQ_PREDIV_CLK failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = lpg->pwm_config.pwm_value & LPG_PWM_VALUE_LSB_MASK;
+ rc = qpnp_lpg_write(lpg, REG_LPG_PWM_VALUE_LSB, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_VALUE_LSB failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = lpg->pwm_config.pwm_value >> 8;
+ mask = LPG_PWM_VALUE_MSB_MASK;
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_PWM_VALUE_MSB, mask, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_VALUE_MSB failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ val = LPG_PWM_VALUE_SYNC;
+ rc = qpnp_lpg_write(lpg, REG_LPG_PWM_SYNC, val);
+ if (rc < 0) {
+ dev_err(lpg->chip->dev, "Write LPG_PWM_SYNC failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static void __qpnp_lpg_calc_pwm_period(int period_ns,
+ struct lpg_pwm_config *pwm_config)
+{
+ struct lpg_pwm_config configs[NUM_PWM_SIZE];
+ int i, j, m, n;
+ int tmp1, tmp2;
+ int clk_period_ns = 0, pwm_clk_period_ns;
+ int clk_delta_ns = INT_MAX, min_clk_delta_ns = INT_MAX;
+ int pwm_period_delta = INT_MAX, min_pwm_period_delta = INT_MAX;
+ int pwm_size_step;
+
+ /*
+ * (2^pwm_size) * (2^pwm_exp) * prediv * NSEC_PER_SEC
+ * pwm_period = ---------------------------------------------------
+ * clk_freq_hz
+ *
+ * Searching the closest settings for the requested PWM period.
+ */
+ for (n = 0; n < ARRAY_SIZE(pwm_size); n++) {
+ pwm_clk_period_ns = period_ns >> pwm_size[n];
+ for (i = ARRAY_SIZE(clk_freq_hz) - 1; i >= 0; i--) {
+ for (j = 0; j < ARRAY_SIZE(clk_prediv); j++) {
+ for (m = 0; m < ARRAY_SIZE(pwm_exponent); m++) {
+ tmp1 = 1 << pwm_exponent[m];
+ tmp1 *= clk_prediv[j];
+ tmp2 = NSEC_PER_SEC / clk_freq_hz[i];
+
+ clk_period_ns = tmp1 * tmp2;
+
+ clk_delta_ns = abs(pwm_clk_period_ns
+ - clk_period_ns);
+ /*
+ * Find the closest setting for
+ * PWM frequency predivide value
+ */
+ if (clk_delta_ns < min_clk_delta_ns) {
+ min_clk_delta_ns
+ = clk_delta_ns;
+ configs[n].pwm_clk
+ = clk_freq_hz[i];
+ configs[n].prediv
+ = clk_prediv[j];
+ configs[n].clk_exp
+ = pwm_exponent[m];
+ configs[n].pwm_size
+ = pwm_size[n];
+ configs[n].best_period_ns
+ = clk_period_ns;
+ }
+ }
+ }
+ }
+
+ configs[n].best_period_ns *= 1 << pwm_size[n];
+ /* Find the closest setting for PWM period */
+ if (min_clk_delta_ns < INT_MAX >> pwm_size[n])
+ pwm_period_delta = min_clk_delta_ns << pwm_size[n];
+ else
+ pwm_period_delta = INT_MAX;
+ if (pwm_period_delta < min_pwm_period_delta) {
+ min_pwm_period_delta = pwm_period_delta;
+ memcpy(pwm_config, &configs[n],
+ sizeof(struct lpg_pwm_config));
+ }
+ }
+
+ /* Larger PWM size can achieve better resolution for PWM duty */
+ for (n = ARRAY_SIZE(pwm_size) - 1; n > 0; n--) {
+ if (pwm_config->pwm_size >= pwm_size[n])
+ break;
+ pwm_size_step = pwm_size[n] - pwm_config->pwm_size;
+ if (pwm_config->clk_exp >= pwm_size_step) {
+ pwm_config->pwm_size = pwm_size[n];
+ pwm_config->clk_exp -= pwm_size_step;
+ }
+ }
+ pr_debug("PWM setting for period_ns %d: pwm_clk = %dHZ, prediv = %d, exponent = %d, pwm_size = %d\n",
+ period_ns, pwm_config->pwm_clk, pwm_config->prediv,
+ pwm_config->clk_exp, pwm_config->pwm_size);
+ pr_debug("Actual period: %dns\n", pwm_config->best_period_ns);
+}
+
+static void __qpnp_lpg_calc_pwm_duty(int period_ns, int duty_ns,
+ struct lpg_pwm_config *pwm_config)
+{
+ u16 pwm_value, max_pwm_value;
+
+ if ((1 << pwm_config->pwm_size) > (INT_MAX / duty_ns))
+ pwm_value = duty_ns / (period_ns >> pwm_config->pwm_size);
+ else
+ pwm_value = (duty_ns << pwm_config->pwm_size) / period_ns;
+
+ max_pwm_value = (1 << pwm_config->pwm_size) - 1;
+ if (pwm_value > max_pwm_value)
+ pwm_value = max_pwm_value;
+ pwm_config->pwm_value = pwm_value;
+}
+
+static int qpnp_lpg_pwm_config(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+ struct qpnp_lpg_channel *lpg;
+ int rc = 0;
+
+ lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return -ENODEV;
+ }
+
+ if (duty_ns > period_ns) {
+ dev_err(pwm_chip->dev, "Duty %dns is larger than period %dns\n",
+ duty_ns, period_ns);
+ return -EINVAL;
+ }
+
+ if (period_ns != lpg->current_period_ns)
+ __qpnp_lpg_calc_pwm_period(period_ns, &lpg->pwm_config);
+
+ if (period_ns != lpg->current_period_ns ||
+ duty_ns != lpg->current_duty_ns)
+ __qpnp_lpg_calc_pwm_duty(period_ns, duty_ns, &lpg->pwm_config);
+
+ rc = qpnp_lpg_set_pwm_config(lpg);
+ if (rc < 0)
+ dev_err(pwm_chip->dev, "Config PWM failed for channel %d, rc=%d\n",
+ lpg->lpg_idx, rc);
+
+ return rc;
+}
+
+static int qpnp_lpg_pwm_enable(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm)
+{
+ struct qpnp_lpg_channel *lpg;
+ int rc = 0;
+ u8 mask, val;
+
+ lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return -ENODEV;
+ }
+
+ mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT;
+ val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT | LPG_EN_LPG_OUT_BIT;
+
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
+ if (rc < 0)
+ dev_err(pwm_chip->dev, "Enable PWM output failed for channel %d, rc=%d\n",
+ lpg->lpg_idx, rc);
+
+ return rc;
+}
+
+static void qpnp_lpg_pwm_disable(struct pwm_chip *pwm_chip,
+ struct pwm_device *pwm)
+{
+ struct qpnp_lpg_channel *lpg;
+ int rc;
+ u8 mask, val;
+
+ lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return;
+ }
+
+ mask = LPG_PWM_SRC_SELECT_MASK | LPG_EN_LPG_OUT_BIT;
+ val = lpg->src_sel << LPG_PWM_SRC_SELECT_SHIFT;
+
+ rc = qpnp_lpg_masked_write(lpg, REG_LPG_ENABLE_CONTROL, mask, val);
+ if (rc < 0)
+ dev_err(pwm_chip->dev, "Disable PWM output failed for channel %d, rc=%d\n",
+ lpg->lpg_idx, rc);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void qpnp_lpg_pwm_dbg_show(struct pwm_chip *pwm_chip, struct seq_file *s)
+{
+ struct qpnp_lpg_channel *lpg;
+ struct lpg_pwm_config *cfg;
+ struct pwm_device *pwm;
+ int i;
+
+ for (i = 0; i < pwm_chip->npwm; i++) {
+ pwm = &pwm_chip->pwms[i];
+
+ lpg = pwm_dev_to_qpnp_lpg(pwm_chip, pwm);
+ if (lpg == NULL) {
+ dev_err(pwm_chip->dev, "lpg not found\n");
+ return;
+ }
+
+ if (test_bit(PWMF_REQUESTED, &pwm->flags)) {
+ seq_printf(s, "LPG %d is requested by %s\n",
+ lpg->lpg_idx + 1, pwm->label);
+ } else {
+ seq_printf(s, "LPG %d is free\n",
+ lpg->lpg_idx + 1);
+ continue;
+ }
+
+ if (pwm_is_enabled(pwm)) {
+ seq_puts(s, " enabled\n");
+ } else {
+ seq_puts(s, " disabled\n");
+ continue;
+ }
+
+ cfg = &lpg->pwm_config;
+ seq_printf(s, " clk = %dHz\n", cfg->pwm_clk);
+ seq_printf(s, " pwm_size = %d\n", cfg->pwm_size);
+ seq_printf(s, " prediv = %d\n", cfg->prediv);
+ seq_printf(s, " exponent = %d\n", cfg->clk_exp);
+ seq_printf(s, " pwm_value = %d\n", cfg->pwm_value);
+ seq_printf(s, " Requested period: %dns, best period = %dns\n",
+ pwm_get_period(pwm), cfg->best_period_ns);
+ }
+}
+#endif
+
+static const struct pwm_ops qpnp_lpg_pwm_ops = {
+ .config = qpnp_lpg_pwm_config,
+ .enable = qpnp_lpg_pwm_enable,
+ .disable = qpnp_lpg_pwm_disable,
+#ifdef CONFIG_DEBUG_FS
+ .dbg_show = qpnp_lpg_pwm_dbg_show,
+#endif
+ .owner = THIS_MODULE,
+};
+
+static int qpnp_lpg_parse_dt(struct qpnp_lpg_chip *chip)
+{
+ int rc = 0, i;
+ u64 base, length;
+ const __be32 *addr;
+
+ addr = of_get_address(chip->dev->of_node, 0, NULL, NULL);
+ if (!addr) {
+ dev_err(chip->dev, "Getting address failed\n");
+ return -EINVAL;
+ }
+ base = be32_to_cpu(addr[0]);
+ length = be32_to_cpu(addr[1]);
+
+ chip->num_lpgs = length / REG_SIZE_PER_LPG;
+ chip->lpgs = devm_kcalloc(chip->dev, chip->num_lpgs,
+ sizeof(*chip->lpgs), GFP_KERNEL);
+ if (!chip->lpgs)
+ return -ENOMEM;
+
+ for (i = 0; i < chip->num_lpgs; i++) {
+ chip->lpgs[i].chip = chip;
+ chip->lpgs[i].lpg_idx = i;
+ chip->lpgs[i].reg_base = base + i * REG_SIZE_PER_LPG;
+ chip->lpgs[i].src_sel = PWM_OUTPUT;
+ }
+
+ return rc;
+}
+
+static int qpnp_lpg_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct qpnp_lpg_chip *chip;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->dev = &pdev->dev;
+ chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
+ if (!chip->regmap) {
+ dev_err(chip->dev, "Getting regmap failed\n");
+ return -EINVAL;
+ }
+
+ rc = qpnp_lpg_parse_dt(chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ dev_set_drvdata(chip->dev, chip);
+
+ mutex_init(&chip->bus_lock);
+ chip->pwm_chip.dev = chip->dev;
+ chip->pwm_chip.base = -1;
+ chip->pwm_chip.npwm = chip->num_lpgs;
+ chip->pwm_chip.ops = &qpnp_lpg_pwm_ops;
+
+ rc = pwmchip_add(&chip->pwm_chip);
+ if (rc < 0) {
+ dev_err(chip->dev, "Add pwmchip failed, rc=%d\n", rc);
+ mutex_destroy(&chip->bus_lock);
+ }
+
+ return rc;
+}
+
+static int qpnp_lpg_remove(struct platform_device *pdev)
+{
+ struct qpnp_lpg_chip *chip = dev_get_drvdata(&pdev->dev);
+ int rc = 0;
+
+ rc = pwmchip_remove(&chip->pwm_chip);
+ if (rc < 0)
+ dev_err(chip->dev, "Remove pwmchip failed, rc=%d\n", rc);
+
+ mutex_destroy(&chip->bus_lock);
+ dev_set_drvdata(chip->dev, NULL);
+
+ return rc;
+}
+
+static const struct of_device_id qpnp_lpg_of_match[] = {
+ { .compatible = "qcom,pwm-lpg",},
+ { },
+};
+
+static struct platform_driver qpnp_lpg_driver = {
+ .driver = {
+ .name = "qcom,pwm-lpg",
+ .of_match_table = qpnp_lpg_of_match,
+ },
+ .probe = qpnp_lpg_probe,
+ .remove = qpnp_lpg_remove,
+};
+module_platform_driver(qpnp_lpg_driver);
+
+MODULE_DESCRIPTION("QTI LPG driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("pwm:pwm-lpg");
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index e72234e..9b5fc50 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -576,9 +576,9 @@
};
struct qeth_ipato {
- int enabled;
- int invert4;
- int invert6;
+ bool enabled;
+ bool invert4;
+ bool invert6;
struct list_head entries;
};
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 838ed62..df8f74c 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -1475,9 +1475,9 @@
qeth_set_intial_options(card);
/* IP address takeover */
INIT_LIST_HEAD(&card->ipato.entries);
- card->ipato.enabled = 0;
- card->ipato.invert4 = 0;
- card->ipato.invert6 = 0;
+ card->ipato.enabled = false;
+ card->ipato.invert4 = false;
+ card->ipato.invert6 = false;
/* init QDIO stuff */
qeth_init_qdio_info(card);
INIT_DELAYED_WORK(&card->buffer_reclaim_work, qeth_buffer_reclaim_work);
diff --git a/drivers/s390/net/qeth_l3.h b/drivers/s390/net/qeth_l3.h
index 26f7953..eedf9b0 100644
--- a/drivers/s390/net/qeth_l3.h
+++ b/drivers/s390/net/qeth_l3.h
@@ -80,7 +80,7 @@
int qeth_l3_add_rxip(struct qeth_card *, enum qeth_prot_versions, const u8 *);
void qeth_l3_del_rxip(struct qeth_card *card, enum qeth_prot_versions,
const u8 *);
-int qeth_l3_is_addr_covered_by_ipato(struct qeth_card *, struct qeth_ipaddr *);
+void qeth_l3_update_ipato(struct qeth_card *card);
struct qeth_ipaddr *qeth_l3_get_addr_buffer(enum qeth_prot_versions);
int qeth_l3_add_ip(struct qeth_card *, struct qeth_ipaddr *);
int qeth_l3_delete_ip(struct qeth_card *, struct qeth_ipaddr *);
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index f91e70c..1487f8a 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -168,8 +168,8 @@
}
}
-int qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card,
- struct qeth_ipaddr *addr)
+static bool qeth_l3_is_addr_covered_by_ipato(struct qeth_card *card,
+ struct qeth_ipaddr *addr)
{
struct qeth_ipato_entry *ipatoe;
u8 addr_bits[128] = {0, };
@@ -178,6 +178,8 @@
if (!card->ipato.enabled)
return 0;
+ if (addr->type != QETH_IP_TYPE_NORMAL)
+ return 0;
qeth_l3_convert_addr_to_bits((u8 *) &addr->u, addr_bits,
(addr->proto == QETH_PROT_IPV4)? 4:16);
@@ -293,8 +295,7 @@
memcpy(addr, tmp_addr, sizeof(struct qeth_ipaddr));
addr->ref_counter = 1;
- if (addr->type == QETH_IP_TYPE_NORMAL &&
- qeth_l3_is_addr_covered_by_ipato(card, addr)) {
+ if (qeth_l3_is_addr_covered_by_ipato(card, addr)) {
QETH_CARD_TEXT(card, 2, "tkovaddr");
addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
}
@@ -607,6 +608,27 @@
/*
* IP address takeover related functions
*/
+
+/**
+ * qeth_l3_update_ipato() - Update 'takeover' property, for all NORMAL IPs.
+ *
+ * Caller must hold ip_lock.
+ */
+void qeth_l3_update_ipato(struct qeth_card *card)
+{
+ struct qeth_ipaddr *addr;
+ unsigned int i;
+
+ hash_for_each(card->ip_htable, i, addr, hnode) {
+ if (addr->type != QETH_IP_TYPE_NORMAL)
+ continue;
+ if (qeth_l3_is_addr_covered_by_ipato(card, addr))
+ addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
+ else
+ addr->set_flags &= ~QETH_IPA_SETIP_TAKEOVER_FLAG;
+ }
+}
+
static void qeth_l3_clear_ipato_list(struct qeth_card *card)
{
struct qeth_ipato_entry *ipatoe, *tmp;
@@ -618,6 +640,7 @@
kfree(ipatoe);
}
+ qeth_l3_update_ipato(card);
spin_unlock_bh(&card->ip_lock);
}
@@ -642,8 +665,10 @@
}
}
- if (!rc)
+ if (!rc) {
list_add_tail(&new->entry, &card->ipato.entries);
+ qeth_l3_update_ipato(card);
+ }
spin_unlock_bh(&card->ip_lock);
@@ -666,6 +691,7 @@
(proto == QETH_PROT_IPV4)? 4:16) &&
(ipatoe->mask_bits == mask_bits)) {
list_del(&ipatoe->entry);
+ qeth_l3_update_ipato(card);
kfree(ipatoe);
}
}
diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c
index cffe42f..d6bdfc6 100644
--- a/drivers/s390/net/qeth_l3_sys.c
+++ b/drivers/s390/net/qeth_l3_sys.c
@@ -372,8 +372,8 @@
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
- struct qeth_ipaddr *addr;
- int i, rc = 0;
+ bool enable;
+ int rc = 0;
if (!card)
return -EINVAL;
@@ -386,25 +386,18 @@
}
if (sysfs_streq(buf, "toggle")) {
- card->ipato.enabled = (card->ipato.enabled)? 0 : 1;
- } else if (sysfs_streq(buf, "1")) {
- card->ipato.enabled = 1;
- hash_for_each(card->ip_htable, i, addr, hnode) {
- if ((addr->type == QETH_IP_TYPE_NORMAL) &&
- qeth_l3_is_addr_covered_by_ipato(card, addr))
- addr->set_flags |=
- QETH_IPA_SETIP_TAKEOVER_FLAG;
- }
- } else if (sysfs_streq(buf, "0")) {
- card->ipato.enabled = 0;
- hash_for_each(card->ip_htable, i, addr, hnode) {
- if (addr->set_flags &
- QETH_IPA_SETIP_TAKEOVER_FLAG)
- addr->set_flags &=
- ~QETH_IPA_SETIP_TAKEOVER_FLAG;
- }
- } else
+ enable = !card->ipato.enabled;
+ } else if (kstrtobool(buf, &enable)) {
rc = -EINVAL;
+ goto out;
+ }
+
+ if (card->ipato.enabled != enable) {
+ card->ipato.enabled = enable;
+ spin_lock_bh(&card->ip_lock);
+ qeth_l3_update_ipato(card);
+ spin_unlock_bh(&card->ip_lock);
+ }
out:
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
@@ -430,20 +423,27 @@
const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
+ bool invert;
int rc = 0;
if (!card)
return -EINVAL;
mutex_lock(&card->conf_mutex);
- if (sysfs_streq(buf, "toggle"))
- card->ipato.invert4 = (card->ipato.invert4)? 0 : 1;
- else if (sysfs_streq(buf, "1"))
- card->ipato.invert4 = 1;
- else if (sysfs_streq(buf, "0"))
- card->ipato.invert4 = 0;
- else
+ if (sysfs_streq(buf, "toggle")) {
+ invert = !card->ipato.invert4;
+ } else if (kstrtobool(buf, &invert)) {
rc = -EINVAL;
+ goto out;
+ }
+
+ if (card->ipato.invert4 != invert) {
+ card->ipato.invert4 = invert;
+ spin_lock_bh(&card->ip_lock);
+ qeth_l3_update_ipato(card);
+ spin_unlock_bh(&card->ip_lock);
+ }
+out:
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
@@ -609,20 +609,27 @@
struct device_attribute *attr, const char *buf, size_t count)
{
struct qeth_card *card = dev_get_drvdata(dev);
+ bool invert;
int rc = 0;
if (!card)
return -EINVAL;
mutex_lock(&card->conf_mutex);
- if (sysfs_streq(buf, "toggle"))
- card->ipato.invert6 = (card->ipato.invert6)? 0 : 1;
- else if (sysfs_streq(buf, "1"))
- card->ipato.invert6 = 1;
- else if (sysfs_streq(buf, "0"))
- card->ipato.invert6 = 0;
- else
+ if (sysfs_streq(buf, "toggle")) {
+ invert = !card->ipato.invert6;
+ } else if (kstrtobool(buf, &invert)) {
rc = -EINVAL;
+ goto out;
+ }
+
+ if (card->ipato.invert6 != invert) {
+ card->ipato.invert6 = invert;
+ spin_lock_bh(&card->ip_lock);
+ qeth_l3_update_ipato(card);
+ spin_unlock_bh(&card->ip_lock);
+ }
+out:
mutex_unlock(&card->conf_mutex);
return rc ? rc : count;
}
diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
index db4e7bb..1b283b2 100644
--- a/drivers/scsi/ufs/Kconfig
+++ b/drivers/scsi/ufs/Kconfig
@@ -83,8 +83,6 @@
tristate "QCOM specific hooks to UFS controller platform driver"
depends on SCSI_UFSHCD_PLATFORM && ARCH_QCOM
select PHY_QCOM_UFS
- select EXTCON
- select EXTCON_GPIO
help
This selects the QCOM specific additions to UFSHCD platform driver.
UFS host on QCOM needs some vendor specific configuration before
diff --git a/drivers/soc/qcom/glink_ssr.c b/drivers/soc/qcom/glink_ssr.c
index 614b53a..239f2c1 100644
--- a/drivers/soc/qcom/glink_ssr.c
+++ b/drivers/soc/qcom/glink_ssr.c
@@ -29,6 +29,7 @@
#define GLINK_SSR_EVENT_INIT ~0
#define NUM_LOG_PAGES 3
+#define GLINK_SSR_PRIORITY 1
#define GLINK_SSR_LOG(x...) do { \
if (glink_ssr_log_ctx) \
ipc_log_string(glink_ssr_log_ctx, x); \
@@ -945,6 +946,7 @@
nb->subsystem = subsys_name;
nb->nb.notifier_call = glink_ssr_restart_notifier_cb;
+ nb->nb.priority = GLINK_SSR_PRIORITY;
handle = subsys_notif_register_notifier(nb->subsystem, &nb->nb);
if (IS_ERR_OR_NULL(handle)) {
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index a28644d..2fb95fe 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -2262,8 +2262,6 @@
set_bit(ICNSS_FW_READY, &penv->state);
- icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_READY, NULL);
-
icnss_pr_info("WLAN FW is ready: 0x%lx\n", penv->state);
icnss_hw_power_off(penv);
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index 7b24c4d..a074763 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -28,6 +28,7 @@
#define SPI_NUM_CHIPSELECT (4)
#define SPI_XFER_TIMEOUT_MS (250)
+#define SPI_AUTO_SUSPEND_DELAY (250)
/* SPI SE specific registers */
#define SE_SPI_CPHA (0x224)
#define SE_SPI_LOOPBACK (0x22C)
@@ -153,6 +154,7 @@
int num_rx_eot;
int num_xfers;
void *ipc;
+ bool shared_se;
};
static struct spi_master *get_spi_master(struct device *dev)
@@ -757,9 +759,22 @@
/* Adjust the AB/IB based on the max speed of the slave.*/
rsc->ib = max_speed * DEFAULT_BUS_WIDTH;
rsc->ab = max_speed * DEFAULT_BUS_WIDTH;
+ if (mas->shared_se) {
+ struct se_geni_rsc *rsc;
+ int ret = 0;
+
+ rsc = &mas->spi_rsc;
+ ret = pinctrl_select_state(rsc->geni_pinctrl,
+ rsc->geni_gpio_active);
+ if (ret)
+ GENI_SE_ERR(mas->ipc, false, NULL,
+ "%s: Error %d pinctrl_select_state\n", __func__, ret);
+ }
+
ret = pm_runtime_get_sync(mas->dev);
if (ret < 0) {
- dev_err(mas->dev, "Error enabling SE resources\n");
+ dev_err(mas->dev, "%s:Error enabling SE resources %d\n",
+ __func__, ret);
pm_runtime_put_noidle(mas->dev);
goto exit_prepare_transfer_hardware;
} else {
@@ -851,6 +866,9 @@
"%s:Major:%d Minor:%d step:%dos%d\n",
__func__, major, minor, step, mas->oversampling);
}
+ mas->shared_se =
+ (geni_read_reg(mas->base, GENI_IF_FIFO_DISABLE_RO) &
+ FIFO_IF_DISABLE);
}
exit_prepare_transfer_hardware:
return ret;
@@ -860,7 +878,20 @@
{
struct spi_geni_master *mas = spi_master_get_devdata(spi);
- pm_runtime_put_sync(mas->dev);
+ if (mas->shared_se) {
+ struct se_geni_rsc *rsc;
+ int ret = 0;
+
+ rsc = &mas->spi_rsc;
+ ret = pinctrl_select_state(rsc->geni_pinctrl,
+ rsc->geni_gpio_sleep);
+ if (ret)
+ GENI_SE_ERR(mas->ipc, false, NULL,
+ "%s: Error %d pinctrl_select_state\n", __func__, ret);
+ }
+
+ pm_runtime_mark_last_busy(mas->dev);
+ pm_runtime_put_autosuspend(mas->dev);
return 0;
}
@@ -1333,6 +1364,9 @@
init_completion(&geni_mas->xfer_done);
init_completion(&geni_mas->tx_cb);
init_completion(&geni_mas->rx_cb);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTO_SUSPEND_DELAY);
+ pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_enable(&pdev->dev);
ret = spi_register_master(spi);
if (ret) {
@@ -1366,7 +1400,14 @@
struct spi_master *spi = get_spi_master(dev);
struct spi_geni_master *geni_mas = spi_master_get_devdata(spi);
- ret = se_geni_resources_off(&geni_mas->spi_rsc);
+ if (geni_mas->shared_se) {
+ ret = se_geni_clks_off(&geni_mas->spi_rsc);
+ if (ret)
+ GENI_SE_ERR(geni_mas->ipc, false, NULL,
+ "%s: Error %d turning off clocks\n", __func__, ret);
+ } else {
+ ret = se_geni_resources_off(&geni_mas->spi_rsc);
+ }
return ret;
}
@@ -1376,7 +1417,14 @@
struct spi_master *spi = get_spi_master(dev);
struct spi_geni_master *geni_mas = spi_master_get_devdata(spi);
- ret = se_geni_resources_on(&geni_mas->spi_rsc);
+ if (geni_mas->shared_se) {
+ ret = se_geni_clks_on(&geni_mas->spi_rsc);
+ if (ret)
+ GENI_SE_ERR(geni_mas->ipc, false, NULL,
+ "%s: Error %d turning on clocks\n", __func__, ret);
+ } else {
+ ret = se_geni_resources_on(&geni_mas->spi_rsc);
+ }
return ret;
}
@@ -1387,9 +1435,29 @@
static int spi_geni_suspend(struct device *dev)
{
- if (!pm_runtime_status_suspended(dev))
- return -EBUSY;
- return 0;
+ int ret = 0;
+
+ if (!pm_runtime_status_suspended(dev)) {
+ struct spi_master *spi = get_spi_master(dev);
+ struct spi_geni_master *geni_mas = spi_master_get_devdata(spi);
+
+ if (list_empty(&spi->queue) && !spi->cur_msg) {
+ GENI_SE_ERR(geni_mas->ipc, true, dev,
+ "%s: Force suspend", __func__);
+ ret = spi_geni_runtime_suspend(dev);
+ if (ret) {
+ GENI_SE_ERR(geni_mas->ipc, true, dev,
+ "Force suspend Failed:%d", ret);
+ } else {
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_enable(dev);
+ }
+ } else {
+ ret = -EBUSY;
+ }
+ }
+ return ret;
}
#else
static int spi_geni_runtime_suspend(struct device *dev)
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index 72e926d..04d2b6e 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -1940,7 +1940,6 @@
struct iscsi_tmr_req *tmr_req;
struct iscsi_tm *hdr;
int out_of_order_cmdsn = 0, ret;
- bool sess_ref = false;
u8 function, tcm_function = TMR_UNKNOWN;
hdr = (struct iscsi_tm *) buf;
@@ -1982,18 +1981,17 @@
buf);
}
+ transport_init_se_cmd(&cmd->se_cmd, &iscsi_ops,
+ conn->sess->se_sess, 0, DMA_NONE,
+ TCM_SIMPLE_TAG, cmd->sense_buffer + 2);
+
+ target_get_sess_cmd(&cmd->se_cmd, true);
+
/*
* TASK_REASSIGN for ERL=2 / connection stays inside of
* LIO-Target $FABRIC_MOD
*/
if (function != ISCSI_TM_FUNC_TASK_REASSIGN) {
- transport_init_se_cmd(&cmd->se_cmd, &iscsi_ops,
- conn->sess->se_sess, 0, DMA_NONE,
- TCM_SIMPLE_TAG, cmd->sense_buffer + 2);
-
- target_get_sess_cmd(&cmd->se_cmd, true);
- sess_ref = true;
-
switch (function) {
case ISCSI_TM_FUNC_ABORT_TASK:
tcm_function = TMR_ABORT_TASK;
@@ -2132,12 +2130,8 @@
* For connection recovery, this is also the default action for
* TMR TASK_REASSIGN.
*/
- if (sess_ref) {
- pr_debug("Handle TMR, using sess_ref=true check\n");
- target_put_sess_cmd(&cmd->se_cmd);
- }
-
iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
+ target_put_sess_cmd(&cmd->se_cmd);
return 0;
}
EXPORT_SYMBOL(iscsit_handle_task_mgt_cmd);
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
index 27dd1e1..14bb2db 100644
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -133,6 +133,15 @@
spin_unlock(&se_cmd->t_state_lock);
return false;
}
+ if (se_cmd->transport_state & CMD_T_PRE_EXECUTE) {
+ if (se_cmd->scsi_status) {
+ pr_debug("Attempted to abort io tag: %llu early failure"
+ " status: 0x%02x\n", se_cmd->tag,
+ se_cmd->scsi_status);
+ spin_unlock(&se_cmd->t_state_lock);
+ return false;
+ }
+ }
if (sess->sess_tearing_down || se_cmd->cmd_wait_set) {
pr_debug("Attempted to abort io tag: %llu already shutdown,"
" skipping\n", se_cmd->tag);
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 4c0782c..6f3eccf 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -1939,6 +1939,7 @@
}
cmd->t_state = TRANSPORT_PROCESSING;
+ cmd->transport_state &= ~CMD_T_PRE_EXECUTE;
cmd->transport_state |= CMD_T_ACTIVE|CMD_T_BUSY|CMD_T_SENT;
spin_unlock_irq(&cmd->t_state_lock);
@@ -2592,6 +2593,7 @@
ret = -ESHUTDOWN;
goto out;
}
+ se_cmd->transport_state |= CMD_T_PRE_EXECUTE;
list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list);
out:
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index f25bade..9e96f8a 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -330,7 +330,7 @@
unsigned long event, void *data)
{
struct cpufreq_policy *policy = data;
- unsigned long clipped_freq, floor_freq;
+ unsigned long clipped_freq = ULONG_MAX, floor_freq = 0;
struct cpufreq_cooling_device *cpufreq_dev;
if (event != CPUFREQ_ADJUST)
@@ -338,31 +338,30 @@
mutex_lock(&cooling_list_lock);
list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) {
- if (!cpumask_test_cpu(policy->cpu, &cpufreq_dev->allowed_cpus))
+ if (!cpumask_intersects(&cpufreq_dev->allowed_cpus,
+ policy->related_cpus))
continue;
-
- /*
- * policy->max is the maximum allowed frequency defined by user
- * and clipped_freq is the maximum that thermal constraints
- * allow.
- *
- * If clipped_freq is lower than policy->max, then we need to
- * readjust policy->max.
- *
- * But, if clipped_freq is greater than policy->max, we don't
- * need to do anything.
- *
- * Similarly, if policy minimum set by the user is less than
- * the floor_frequency, then adjust the policy->min.
- */
- clipped_freq = cpufreq_dev->clipped_freq;
- floor_freq = cpufreq_dev->floor_freq;
-
- if (policy->max > clipped_freq || policy->min < floor_freq)
- cpufreq_verify_within_limits(policy, floor_freq,
- clipped_freq);
- break;
+ if (cpufreq_dev->clipped_freq < clipped_freq)
+ clipped_freq = cpufreq_dev->clipped_freq;
+ if (cpufreq_dev->floor_freq > floor_freq)
+ floor_freq = cpufreq_dev->floor_freq;
}
+ /*
+ * policy->max is the maximum allowed frequency defined by user
+ * and clipped_freq is the maximum that thermal constraints
+ * allow.
+ *
+ * If clipped_freq is lower than policy->max, then we need to
+ * readjust policy->max.
+ *
+ * But, if clipped_freq is greater than policy->max, we don't
+ * need to do anything.
+ *
+ * Similarly, if policy minimum set by the user is less than
+ * the floor_frequency, then adjust the policy->min.
+ */
+ if (policy->max > clipped_freq || policy->min < floor_freq)
+ cpufreq_verify_within_limits(policy, floor_freq, clipped_freq);
mutex_unlock(&cooling_list_lock);
return NOTIFY_OK;
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index bdf0e6e..faf50df 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -1764,7 +1764,7 @@
{
struct n_tty_data *ldata = tty->disc_data;
- if (!old || (old->c_lflag ^ tty->termios.c_lflag) & ICANON) {
+ if (!old || (old->c_lflag ^ tty->termios.c_lflag) & (ICANON | EXTPROC)) {
bitmap_zero(ldata->read_flags, N_TTY_BUF_SIZE);
ldata->line_start = ldata->read_tail;
if (!L_ICANON(tty) || !read_cnt(ldata)) {
@@ -2427,7 +2427,7 @@
return put_user(tty_chars_in_buffer(tty), (int __user *) arg);
case TIOCINQ:
down_write(&tty->termios_rwsem);
- if (L_ICANON(tty))
+ if (L_ICANON(tty) && !L_EXTPROC(tty))
retval = inq_canon(ldata);
else
retval = read_cnt(ldata);
diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index d79b95764..185a9e2 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -894,7 +894,6 @@
msm_geni_serial_prep_dma_tx(uport);
}
- IPC_LOG_MSG(msm_port->ipc_log_misc, "%s\n", __func__);
return;
check_flow_ctrl:
geni_ios = geni_read_reg_nolog(uport->membase, SE_GENI_IOS);
@@ -963,7 +962,18 @@
SE_GENI_M_IRQ_CLEAR);
}
geni_write_reg_nolog(M_CMD_CANCEL_EN, uport, SE_GENI_M_IRQ_CLEAR);
- IPC_LOG_MSG(port->ipc_log_misc, "%s\n", __func__);
+ /*
+ * If we end up having to cancel an on-going Tx for non-console usecase
+ * then it means there was some unsent data in the Tx FIFO, consequently
+ * it means that there is a vote imbalance as we put in a vote during
+ * start_tx() that is removed only as part of a "done" ISR. To balance
+ * this out, remove the vote put in during start_tx().
+ */
+ if (!uart_console(uport)) {
+ IPC_LOG_MSG(port->ipc_log_misc, "%s:Removing vote\n", __func__);
+ msm_geni_serial_power_off(uport);
+ }
+ IPC_LOG_MSG(port->ipc_log_misc, "%s:\n", __func__);
}
static void msm_geni_serial_stop_tx(struct uart_port *uport)
@@ -2529,6 +2539,8 @@
* doing a stop_rx else we could end up flowing off the peer.
*/
mb();
+ IPC_LOG_MSG(port->ipc_log_pwr, "%s: Manual Flow ON 0x%x\n",
+ __func__, uart_manual_rfr);
}
stop_rx_sequencer(&port->uport);
if ((geni_status & M_GENI_CMD_ACTIVE))
@@ -2610,6 +2622,7 @@
mutex_unlock(&tty_port->mutex);
return -EBUSY;
}
+ IPC_LOG_MSG(port->ipc_log_pwr, "%s\n", __func__);
mutex_unlock(&tty_port->mutex);
}
return 0;
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index c220c2c..e99f1c5 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -446,7 +446,7 @@
* Callers other than flush_to_ldisc() need to exclude the kworker
* from concurrent use of the line discipline, see paste_selection().
*
- * Returns the number of bytes not processed
+ * Returns the number of bytes processed
*/
int tty_ldisc_receive_buf(struct tty_ldisc *ld, unsigned char *p,
char *f, int count)
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 734a635..8d9f9a8 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -1543,6 +1543,9 @@
"%s: %s driver does not set tty->port. This will crash the kernel later. Fix the driver!\n",
__func__, tty->driver->name);
+ retval = tty_ldisc_lock(tty, 5 * HZ);
+ if (retval)
+ goto err_release_lock;
tty->port->itty = tty;
/*
@@ -1553,6 +1556,7 @@
retval = tty_ldisc_setup(tty, tty->link);
if (retval)
goto err_release_tty;
+ tty_ldisc_unlock(tty);
/* Return the tty locked so that it cannot vanish under the caller */
return tty;
@@ -1565,9 +1569,11 @@
/* call the tty release_tty routine to clean out this slot */
err_release_tty:
- tty_unlock(tty);
+ tty_ldisc_unlock(tty);
tty_info_ratelimited(tty, "ldisc open failed (%d), clearing slot %d\n",
retval, idx);
+err_release_lock:
+ tty_unlock(tty);
release_tty(tty, idx);
return ERR_PTR(retval);
}
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index b0500a0..3a9e2a2 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -336,7 +336,7 @@
ldsem_up_write(&tty->ldisc_sem);
}
-static int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
+int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout)
{
int ret;
@@ -347,7 +347,7 @@
return 0;
}
-static void tty_ldisc_unlock(struct tty_struct *tty)
+void tty_ldisc_unlock(struct tty_struct *tty)
{
clear_bit(TTY_LDISC_HALTED, &tty->flags);
__tty_ldisc_unlock(tty);
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 23b7823..0dce6ab 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -1002,7 +1002,7 @@
case USB_SSP_CAP_TYPE:
ssp_cap = (struct usb_ssp_cap_descriptor *)buffer;
ssac = (le32_to_cpu(ssp_cap->bmAttributes) &
- USB_SSP_SUBLINK_SPEED_ATTRIBS) + 1;
+ USB_SSP_SUBLINK_SPEED_ATTRIBS);
if (length >= USB_DT_USB_SSP_CAP_SIZE(ssac))
dev->bos->ssp_cap = ssp_cap;
break;
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 5001028..c05c4f8 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -57,10 +57,11 @@
/* Microsoft LifeCam-VX700 v2.0 */
{ USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME },
- /* Logitech HD Pro Webcams C920, C920-C and C930e */
+ /* Logitech HD Pro Webcams C920, C920-C, C925e and C930e */
{ USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT },
{ USB_DEVICE(0x046d, 0x0841), .driver_info = USB_QUIRK_DELAY_INIT },
{ USB_DEVICE(0x046d, 0x0843), .driver_info = USB_QUIRK_DELAY_INIT },
+ { USB_DEVICE(0x046d, 0x085b), .driver_info = USB_QUIRK_DELAY_INIT },
/* Logitech ConferenceCam CC3000e */
{ USB_DEVICE(0x046d, 0x0847), .driver_info = USB_QUIRK_DELAY_INIT },
@@ -154,6 +155,9 @@
/* Genesys Logic hub, internally used by KY-688 USB 3.1 Type-C Hub */
{ USB_DEVICE(0x05e3, 0x0612), .driver_info = USB_QUIRK_NO_LPM },
+ /* ELSA MicroLink 56K */
+ { USB_DEVICE(0x05cc, 0x2267), .driver_info = USB_QUIRK_RESET_RESUME },
+
/* Genesys Logic hub, internally used by Moshi USB to Ethernet Adapter */
{ USB_DEVICE(0x05e3, 0x0616), .driver_info = USB_QUIRK_NO_LPM },
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 465a1c6..e0321a1 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -903,6 +903,16 @@
dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
}
+ /*
+ * Enable hardware control of sending remote wakeup in HS when
+ * the device is in the L1 state.
+ */
+ if (dwc->revision >= DWC3_REVISION_290A) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
+ reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
+ dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
+ }
+
return 0;
err2:
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index e1dc7c8..b91642a 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -217,6 +217,9 @@
/* Global Debug LTSSM Register */
#define DWC3_GDBGLTSSM_LINKSTATE_MASK (0xF << 22)
+/* Global User Control 1 Register */
+#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW (1 << 24)
+
/* Global USB2 PHY Configuration Register */
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
#define DWC3_GUSB2PHYCFG_ENBLSLPM (1 << 8)
@@ -1036,6 +1039,7 @@
#define DWC3_REVISION_260A 0x5533260a
#define DWC3_REVISION_270A 0x5533270a
#define DWC3_REVISION_280A 0x5533280a
+#define DWC3_REVISION_290A 0x5533290a
#define DWC3_REVISION_300A 0x5533300a
#define DWC3_REVISION_310A 0x5533310a
#define DWC3_REVISION_320A 0x5533320a
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index cc239b0..5c4a24d 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -3331,6 +3331,8 @@
* turning on usb gdsc regulator clk is stuck off.
*/
dwc3_msm_config_gdsc(mdwc, 1);
+ clk_prepare_enable(mdwc->iface_clk);
+ clk_prepare_enable(mdwc->core_clk);
clk_prepare_enable(mdwc->cfg_ahb_clk);
/* Configure AHB2PHY for one wait state read/write*/
val = readl_relaxed(mdwc->ahb2phy_base +
@@ -3343,6 +3345,8 @@
mb();
}
clk_disable_unprepare(mdwc->cfg_ahb_clk);
+ clk_disable_unprepare(mdwc->core_clk);
+ clk_disable_unprepare(mdwc->iface_clk);
dwc3_msm_config_gdsc(mdwc, 0);
}
}
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index cbce880..986c97c 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -816,6 +816,16 @@
if (!dwc->gadget_driver)
goto out;
+ /*
+ * Workaround for SNPS STAR: 9001046257 which affects dwc3 core
+ * 3.10a or earlier. LPM Not rejected during control transfer. Device
+ * is programmed to reject LPM when SETUP packet is received and
+ * ACK LPM after completing STATUS stage.
+ */
+ if (dwc->has_lpm_erratum && dwc->revision <= DWC3_REVISION_310A)
+ dwc3_masked_write_readback(dwc->regs, DWC3_DCTL,
+ DWC3_DCTL_LPM_ERRATA_MASK, DWC3_DCTL_LPM_ERRATA(0));
+
trace_dwc3_ctrl_req(ctrl);
len = le16_to_cpu(ctrl->wLength);
@@ -990,6 +1000,11 @@
dbg_print(dep->number, "DONE", status, "STATUS");
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
+
+ if (dwc->has_lpm_erratum && dwc->revision <= DWC3_REVISION_310A)
+ dwc3_masked_write_readback(dwc->regs, DWC3_DCTL,
+ DWC3_DCTL_LPM_ERRATA_MASK,
+ DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold));
}
static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index a06f9a8..4911253 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -64,4 +64,28 @@
base - DWC3_GLOBALS_REGS_START + offset, value);
}
+static inline void dwc3_masked_write_readback(void __iomem *base,
+ u32 offset, const u32 mask, u32 value)
+{
+ u32 write_val, tmp;
+
+ tmp = readl_relaxed(base + offset - DWC3_GLOBALS_REGS_START);
+ tmp &= ~mask; /* retain other bits */
+ write_val = tmp | value;
+
+ writel_relaxed(write_val, base + offset - DWC3_GLOBALS_REGS_START);
+
+ /* Read back to see if value was written */
+ tmp = readl_relaxed(base + offset);
+
+ dwc3_trace(trace_dwc3_masked_write_readback,
+ "addr %p readback val %08x",
+ base - DWC3_GLOBALS_REGS_START + offset, tmp);
+
+ tmp &= mask; /* clear other bits */
+ if (tmp != value)
+ pr_err("%s: write: %x to %x FAILED\n",
+ __func__, value, offset);
+}
+
#endif /* __DRIVERS_USB_DWC3_IO_H */
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
index d24cefd..88f5fb8 100644
--- a/drivers/usb/dwc3/trace.h
+++ b/drivers/usb/dwc3/trace.h
@@ -47,6 +47,11 @@
TP_ARGS(vaf)
);
+DEFINE_EVENT(dwc3_log_msg, dwc3_masked_write_readback,
+ TP_PROTO(struct va_format *vaf),
+ TP_ARGS(vaf)
+);
+
DEFINE_EVENT(dwc3_log_msg, dwc3_gadget,
TP_PROTO(struct va_format *vaf),
TP_ARGS(vaf)
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index dc368c7..37903f9 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -300,15 +300,13 @@
gsi_channel_info.gevntcount_hi_addr;
in_params->dir = GSI_CHAN_DIR_FROM_GSI;
in_params->xfer_ring_len = gsi_channel_info.xfer_ring_len;
- in_params->xfer_ring_base_addr = gsi_channel_info.xfer_ring_base_addr;
in_params->xfer_scratch.last_trb_addr_iova =
gsi_channel_info.last_trb_addr;
- in_params->xfer_ring_base_addr = in_params->xfer_ring_base_addr_iova =
+ in_params->xfer_ring_base_addr_iova =
gsi_channel_info.xfer_ring_base_addr;
in_params->data_buff_base_len = d_port->in_request.buf_len *
d_port->in_request.num_bufs;
- in_params->data_buff_base_addr = in_params->data_buff_base_addr_iova =
- d_port->in_request.dma;
+ in_params->data_buff_base_addr_iova = d_port->in_request.dma;
in_params->xfer_scratch.const_buffer_size =
gsi_channel_info.const_buffer_size;
in_params->xfer_scratch.depcmd_low_addr =
@@ -340,13 +338,11 @@
out_params->dir = GSI_CHAN_DIR_TO_GSI;
out_params->xfer_ring_len =
gsi_channel_info.xfer_ring_len;
- out_params->xfer_ring_base_addr =
- out_params->xfer_ring_base_addr_iova =
+ out_params->xfer_ring_base_addr_iova =
gsi_channel_info.xfer_ring_base_addr;
out_params->data_buff_base_len = d_port->out_request.buf_len *
d_port->out_request.num_bufs;
- out_params->data_buff_base_addr =
- out_params->data_buff_base_addr_iova =
+ out_params->data_buff_base_addr_iova =
d_port->out_request.dma;
out_params->xfer_scratch.last_trb_addr_iova =
gsi_channel_info.last_trb_addr;
@@ -3069,7 +3065,7 @@
ipa_chnl_params->xfer_ring_len);
len += scnprintf(buf + len, PAGE_SIZE - len,
"%25s %10x\n", "IN TRB Base Addr: ", (unsigned int)
- ipa_chnl_params->xfer_ring_base_addr);
+ ipa_chnl_params->xfer_ring_base_addr_iova);
len += scnprintf(buf + len, PAGE_SIZE - len,
"%25s %10x\n", "GEVENTCNTLO IN Addr: ",
ipa_chnl_params->gevntcount_low_addr);
@@ -3103,7 +3099,7 @@
ipa_chnl_params->xfer_ring_len);
len += scnprintf(buf + len, PAGE_SIZE - len,
"%25s %10x\n", "OUT TRB Base Addr: ", (unsigned int)
- ipa_chnl_params->xfer_ring_base_addr);
+ ipa_chnl_params->xfer_ring_base_addr_iova);
len += scnprintf(buf + len, PAGE_SIZE - len,
"%25s %10x\n", "GEVENTCNTLO OUT Addr: ",
ipa_chnl_params->gevntcount_low_addr);
diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c
index a0fecb2..6ae2693 100644
--- a/drivers/usb/gadget/function/f_qdss.c
+++ b/drivers/usb/gadget/function/f_qdss.c
@@ -183,15 +183,28 @@
}
/*----------------------------------------------------------------------*/
-static void qdss_ctrl_write_complete(struct usb_ep *ep,
+static void qdss_write_complete(struct usb_ep *ep,
struct usb_request *req)
{
struct f_qdss *qdss = ep->driver_data;
struct qdss_request *d_req = req->context;
+ struct usb_ep *in;
+ struct list_head *list_pool;
+ enum qdss_state state;
unsigned long flags;
pr_debug("qdss_ctrl_write_complete\n");
+ if (qdss->debug_inface_enabled) {
+ in = qdss->port.ctrl_in;
+ list_pool = &qdss->ctrl_write_pool;
+ state = USB_QDSS_CTRL_WRITE_DONE;
+ } else {
+ in = qdss->port.data;
+ list_pool = &qdss->data_write_pool;
+ state = USB_QDSS_DATA_WRITE_DONE;
+ }
+
if (!req->status) {
/* send zlp */
if ((req->length >= ep->maxpacket) &&
@@ -199,13 +212,13 @@
req->length = 0;
d_req->actual = req->actual;
d_req->status = req->status;
- if (!usb_ep_queue(qdss->port.ctrl_in, req, GFP_ATOMIC))
+ if (!usb_ep_queue(in, req, GFP_ATOMIC))
return;
}
}
spin_lock_irqsave(&qdss->lock, flags);
- list_add_tail(&req->list, &qdss->ctrl_write_pool);
+ list_add_tail(&req->list, list_pool);
if (req->length != 0) {
d_req->actual = req->actual;
d_req->status = req->status;
@@ -213,8 +226,7 @@
spin_unlock_irqrestore(&qdss->lock, flags);
if (qdss->ch.notify)
- qdss->ch.notify(qdss->ch.priv, USB_QDSS_CTRL_WRITE_DONE, d_req,
- NULL);
+ qdss->ch.notify(qdss->ch.priv, state, d_req, NULL);
}
static void qdss_ctrl_read_complete(struct usb_ep *ep,
@@ -252,6 +264,12 @@
return;
}
+ list_for_each_safe(act, tmp, &qdss->data_write_pool) {
+ req = list_entry(act, struct usb_request, list);
+ list_del(&req->list);
+ usb_ep_free_request(qdss->port.data, req);
+ }
+
list_for_each_safe(act, tmp, &qdss->ctrl_write_pool) {
req = list_entry(act, struct usb_request, list);
list_del(&req->list);
@@ -271,23 +289,41 @@
{
struct f_qdss *qdss = ch->priv_usb;
struct usb_request *req;
+ struct usb_ep *in;
+ struct list_head *list_pool;
int i;
pr_debug("usb_qdss_alloc_req\n");
- if (no_write_buf <= 0 || no_read_buf <= 0 || !qdss) {
+ if (!qdss) {
+ pr_err("usb_qdss_alloc_req: channel %s closed\n", ch->name);
+ return -ENODEV;
+ }
+
+ if ((qdss->debug_inface_enabled &&
+ (no_write_buf <= 0 || no_read_buf <= 0)) ||
+ (!qdss->debug_inface_enabled &&
+ (no_write_buf <= 0 || no_read_buf))) {
pr_err("usb_qdss_alloc_req: missing params\n");
return -ENODEV;
}
+ if (qdss->debug_inface_enabled) {
+ in = qdss->port.ctrl_in;
+ list_pool = &qdss->ctrl_write_pool;
+ } else {
+ in = qdss->port.data;
+ list_pool = &qdss->data_write_pool;
+ }
+
for (i = 0; i < no_write_buf; i++) {
- req = usb_ep_alloc_request(qdss->port.ctrl_in, GFP_ATOMIC);
+ req = usb_ep_alloc_request(in, GFP_ATOMIC);
if (!req) {
pr_err("usb_qdss_alloc_req: ctrl_in allocation err\n");
goto fail;
}
- req->complete = qdss_ctrl_write_complete;
- list_add_tail(&req->list, &qdss->ctrl_write_pool);
+ req->complete = qdss_write_complete;
+ list_add_tail(&req->list, list_pool);
}
for (i = 0; i < no_read_buf; i++) {
@@ -378,6 +414,10 @@
qdss_ctrl_intf_desc.iInterface = id;
}
+ /* for non-accelerated path keep tx fifo size 1k */
+ if (!strcmp(qdss->ch.name, USB_QDSS_CH_MDM))
+ qdss_data_ep_comp_desc.bMaxBurst = 0;
+
ep = usb_ep_autoconfig_ss(gadget, &qdss_ss_data_desc,
&qdss_data_ep_comp_desc);
if (!ep) {
@@ -490,21 +530,20 @@
qdss = container_of(work, struct f_qdss, disconnect_w);
pr_debug("usb_qdss_disconnect_work\n");
- /*
- * Uninitialized init data i.e. ep specific operation.
- * Notify qdss to cancel all active transfers.
- */
- if (qdss->ch.app_conn) {
+
+ /* Notify qdss to cancel all active transfers */
+ if (qdss->ch.notify)
+ qdss->ch.notify(qdss->ch.priv,
+ USB_QDSS_DISCONNECT,
+ NULL,
+ NULL);
+
+ /* Uninitialized init data i.e. ep specific operation */
+ if (qdss->ch.app_conn && !strcmp(qdss->ch.name, USB_QDSS_CH_MSM)) {
status = uninit_data(qdss->port.data);
if (status)
pr_err("%s: uninit_data error\n", __func__);
- if (qdss->ch.notify)
- qdss->ch.notify(qdss->ch.priv,
- USB_QDSS_DISCONNECT,
- NULL,
- NULL);
-
status = set_qdss_data_connection(qdss, 0);
if (status)
pr_err("qdss_disconnect error");
@@ -561,15 +600,16 @@
}
pr_debug("usb_qdss_connect_work\n");
+
+ if (!strcmp(qdss->ch.name, USB_QDSS_CH_MDM))
+ goto notify;
+
status = set_qdss_data_connection(qdss, 1);
if (status) {
pr_err("set_qdss_data_connection error(%d)", status);
return;
}
- if (qdss->ch.notify)
- qdss->ch.notify(qdss->ch.priv, USB_QDSS_CONNECT,
- NULL, &qdss->ch);
spin_lock_irqsave(&qdss->lock, flags);
req = qdss->endless_req;
spin_unlock_irqrestore(&qdss->lock, flags);
@@ -577,8 +617,15 @@
return;
status = usb_ep_queue(qdss->port.data, req, GFP_ATOMIC);
- if (status)
+ if (status) {
pr_err("%s: usb_ep_queue error (%d)\n", __func__, status);
+ return;
+ }
+
+notify:
+ if (qdss->ch.notify)
+ qdss->ch.notify(qdss->ch.priv, USB_QDSS_CONNECT,
+ NULL, &qdss->ch);
}
static int qdss_set_alt(struct usb_function *f, unsigned int intf,
@@ -718,6 +765,7 @@
spin_lock_init(&qdss->lock);
INIT_LIST_HEAD(&qdss->ctrl_read_pool);
INIT_LIST_HEAD(&qdss->ctrl_write_pool);
+ INIT_LIST_HEAD(&qdss->data_write_pool);
INIT_WORK(&qdss->connect_w, usb_qdss_connect_work);
INIT_WORK(&qdss->disconnect_w, usb_qdss_disconnect_work);
@@ -813,6 +861,50 @@
}
EXPORT_SYMBOL(usb_qdss_ctrl_write);
+int usb_qdss_write(struct usb_qdss_ch *ch, struct qdss_request *d_req)
+{
+ struct f_qdss *qdss = ch->priv_usb;
+ unsigned long flags;
+ struct usb_request *req = NULL;
+
+ pr_debug("usb_qdss_ctrl_write\n");
+
+ if (!qdss)
+ return -ENODEV;
+
+ spin_lock_irqsave(&qdss->lock, flags);
+
+ if (qdss->usb_connected == 0) {
+ spin_unlock_irqrestore(&qdss->lock, flags);
+ return -EIO;
+ }
+
+ if (list_empty(&qdss->data_write_pool)) {
+ pr_err("error: usb_qdss_data_write list is empty\n");
+ spin_unlock_irqrestore(&qdss->lock, flags);
+ return -EAGAIN;
+ }
+
+ req = list_first_entry(&qdss->data_write_pool, struct usb_request,
+ list);
+ list_del(&req->list);
+ spin_unlock_irqrestore(&qdss->lock, flags);
+
+ req->buf = d_req->buf;
+ req->length = d_req->length;
+ req->context = d_req;
+ if (usb_ep_queue(qdss->port.data, req, GFP_ATOMIC)) {
+ spin_lock_irqsave(&qdss->lock, flags);
+ list_add_tail(&req->list, &qdss->data_write_pool);
+ spin_unlock_irqrestore(&qdss->lock, flags);
+ pr_err("qdss usb_ep_queue failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_qdss_write);
+
struct usb_qdss_ch *usb_qdss_open(const char *name, void *priv,
void (*notify)(void *priv, unsigned int event,
struct qdss_request *d_req, struct usb_qdss_ch *))
@@ -870,7 +962,9 @@
pr_debug("usb_qdss_close\n");
spin_lock_irqsave(&qdss_lock, flags);
- if (!qdss || !qdss->usb_connected) {
+ ch->priv_usb = NULL;
+ if (!qdss || !qdss->usb_connected ||
+ !strcmp(qdss->ch.name, USB_QDSS_CH_MDM)) {
ch->app_conn = 0;
spin_unlock_irqrestore(&qdss_lock, flags);
return;
diff --git a/drivers/usb/gadget/function/f_qdss.h b/drivers/usb/gadget/function/f_qdss.h
index 72edb90..57c76f8 100644
--- a/drivers/usb/gadget/function/f_qdss.h
+++ b/drivers/usb/gadget/function/f_qdss.h
@@ -59,6 +59,10 @@
struct usb_qdss_ch ch;
struct list_head ctrl_read_pool;
struct list_head ctrl_write_pool;
+
+ /* for mdm channel SW path */
+ struct list_head data_write_pool;
+
struct work_struct connect_w;
struct work_struct disconnect_w;
spinlock_t lock;
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index b59d214..2911b72 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1086,7 +1086,8 @@
return 1;
fail:
-
+ if (dev->eps[0].ring)
+ xhci_ring_free(xhci, dev->eps[0].ring);
if (dev->in_ctx)
xhci_free_container_ctx(xhci, dev->in_ctx);
if (dev->out_ctx)
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index c87ef38..f6782a3 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -190,6 +190,9 @@
xhci->quirks |= XHCI_BROKEN_STREAMS;
}
if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
+ pdev->device == 0x0014)
+ xhci->quirks |= XHCI_TRUST_TX_LENGTH;
+ if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
pdev->device == 0x0015)
xhci->quirks |= XHCI_RESET_ON_RESUME;
if (pdev->vendor == PCI_VENDOR_ID_VIA)
diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c
index 8e7737d..03be5d5 100644
--- a/drivers/usb/misc/usb3503.c
+++ b/drivers/usb/misc/usb3503.c
@@ -292,6 +292,8 @@
if (gpio_is_valid(hub->gpio_reset)) {
err = devm_gpio_request_one(dev, hub->gpio_reset,
GPIOF_OUT_INIT_LOW, "usb3503 reset");
+ /* Datasheet defines a hardware reset to be at least 100us */
+ usleep_range(100, 10000);
if (err) {
dev_err(dev,
"unable to request GPIO %d as reset pin (%d)\n",
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index 1a874a1..80b37d2 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -1002,7 +1002,9 @@
break;
case MON_IOCQ_RING_SIZE:
+ mutex_lock(&rp->fetch_lock);
ret = rp->b_size;
+ mutex_unlock(&rp->fetch_lock);
break;
case MON_IOCT_RING_SIZE:
@@ -1229,12 +1231,16 @@
unsigned long offset, chunk_idx;
struct page *pageptr;
+ mutex_lock(&rp->fetch_lock);
offset = vmf->pgoff << PAGE_SHIFT;
- if (offset >= rp->b_size)
+ if (offset >= rp->b_size) {
+ mutex_unlock(&rp->fetch_lock);
return VM_FAULT_SIGBUS;
+ }
chunk_idx = offset / CHUNK_SIZE;
pageptr = rp->b_vec[chunk_idx].pg;
get_page(pageptr);
+ mutex_unlock(&rp->fetch_lock);
vmf->page = pageptr;
return 0;
}
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 11ee55e..3178d8a 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -121,6 +121,7 @@
{ USB_DEVICE(0x10C4, 0x8470) }, /* Juniper Networks BX Series System Console */
{ USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */
{ USB_DEVICE(0x10C4, 0x84B6) }, /* Starizona Hyperion */
+ { USB_DEVICE(0x10C4, 0x85A7) }, /* LifeScan OneTouch Verio IQ */
{ USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */
{ USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */
{ USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */
@@ -171,6 +172,7 @@
{ USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */
{ USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */
{ USB_DEVICE(0x18EF, 0xE025) }, /* ELV Marble Sound Board 1 */
+ { USB_DEVICE(0x18EF, 0xE030) }, /* ELV ALC 8xxx Battery Charger */
{ USB_DEVICE(0x18EF, 0xE032) }, /* ELV TFD500 Data Logger */
{ USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */
{ USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 3249f42..0c743e4 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1017,6 +1017,7 @@
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_BT_USB_PID) },
{ USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_WL_USB_PID) },
+ { USB_DEVICE(AIRBUS_DS_VID, AIRBUS_DS_P8GR) },
{ } /* Terminating entry */
};
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index f9d15bd..543d280 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -914,6 +914,12 @@
#define ICPDAS_I7563U_PID 0x0105
/*
+ * Airbus Defence and Space
+ */
+#define AIRBUS_DS_VID 0x1e8e /* Vendor ID */
+#define AIRBUS_DS_P8GR 0x6001 /* Tetra P8GR */
+
+/*
* RT Systems programming cables for various ham radios
*/
#define RTSYSTEMS_VID 0x2100 /* Vendor ID */
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index ffa8ec9..a818c43 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -236,6 +236,8 @@
/* These Quectel products use Qualcomm's vendor ID */
#define QUECTEL_PRODUCT_UC20 0x9003
#define QUECTEL_PRODUCT_UC15 0x9090
+/* These Yuga products use Qualcomm's vendor ID */
+#define YUGA_PRODUCT_CLM920_NC5 0x9625
#define QUECTEL_VENDOR_ID 0x2c7c
/* These Quectel products use Quectel's vendor ID */
@@ -283,6 +285,7 @@
#define TELIT_PRODUCT_LE922_USBCFG3 0x1043
#define TELIT_PRODUCT_LE922_USBCFG5 0x1045
#define TELIT_PRODUCT_ME910 0x1100
+#define TELIT_PRODUCT_ME910_DUAL_MODEM 0x1101
#define TELIT_PRODUCT_LE920 0x1200
#define TELIT_PRODUCT_LE910 0x1201
#define TELIT_PRODUCT_LE910_USBCFG4 0x1206
@@ -648,6 +651,11 @@
.reserved = BIT(1) | BIT(3),
};
+static const struct option_blacklist_info telit_me910_dual_modem_blacklist = {
+ .sendsetup = BIT(0),
+ .reserved = BIT(3),
+};
+
static const struct option_blacklist_info telit_le910_blacklist = {
.sendsetup = BIT(0),
.reserved = BIT(1) | BIT(2),
@@ -677,6 +685,10 @@
.reserved = BIT(4) | BIT(5),
};
+static const struct option_blacklist_info yuga_clm920_nc5_blacklist = {
+ .reserved = BIT(1) | BIT(4),
+};
+
static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
{ USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
@@ -1181,6 +1193,9 @@
{ USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC15)},
{ USB_DEVICE(QUALCOMM_VENDOR_ID, QUECTEL_PRODUCT_UC20),
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ /* Yuga products use Qualcomm vendor ID */
+ { USB_DEVICE(QUALCOMM_VENDOR_ID, YUGA_PRODUCT_CLM920_NC5),
+ .driver_info = (kernel_ulong_t)&yuga_clm920_nc5_blacklist },
/* Quectel products using Quectel vendor ID */
{ USB_DEVICE(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC21),
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
@@ -1247,6 +1262,8 @@
.driver_info = (kernel_ulong_t)&telit_le922_blacklist_usbcfg0 },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910),
.driver_info = (kernel_ulong_t)&telit_me910_blacklist },
+ { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_ME910_DUAL_MODEM),
+ .driver_info = (kernel_ulong_t)&telit_me910_dual_modem_blacklist },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910),
.driver_info = (kernel_ulong_t)&telit_le910_blacklist },
{ USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE910_USBCFG4),
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 4516291..fb6dc16 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -166,6 +166,8 @@
{DEVICE_SWI(0x1199, 0x9079)}, /* Sierra Wireless EM74xx */
{DEVICE_SWI(0x1199, 0x907a)}, /* Sierra Wireless EM74xx QDL */
{DEVICE_SWI(0x1199, 0x907b)}, /* Sierra Wireless EM74xx */
+ {DEVICE_SWI(0x1199, 0x9090)}, /* Sierra Wireless EM7565 QDL */
+ {DEVICE_SWI(0x1199, 0x9091)}, /* Sierra Wireless EM7565 */
{DEVICE_SWI(0x413c, 0x81a2)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a3)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */
{DEVICE_SWI(0x413c, 0x81a4)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */
@@ -346,6 +348,7 @@
break;
case 2:
dev_dbg(dev, "NMEA GPS interface found\n");
+ sendsetup = true;
break;
case 3:
dev_dbg(dev, "Modem port found\n");
diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h
index 9f356f7..719ec68 100644
--- a/drivers/usb/storage/unusual_uas.h
+++ b/drivers/usb/storage/unusual_uas.h
@@ -156,6 +156,13 @@
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_ATA_1X),
+/* Reported-by: Icenowy Zheng <icenowy@aosc.io> */
+UNUSUAL_DEV(0x2537, 0x1068, 0x0000, 0x9999,
+ "Norelsys",
+ "NS1068X",
+ USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ US_FL_IGNORE_UAS),
+
/* Reported-by: Takeo Nakayama <javhera@gmx.com> */
UNUSUAL_DEV(0x357d, 0x7788, 0x0000, 0x9999,
"JMicron",
diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c
index c653ce5..1886d8e 100644
--- a/drivers/usb/usbip/stub_dev.c
+++ b/drivers/usb/usbip/stub_dev.c
@@ -163,8 +163,7 @@
* step 1?
*/
if (ud->tcp_socket) {
- dev_dbg(&sdev->udev->dev, "shutdown tcp_socket %p\n",
- ud->tcp_socket);
+ dev_dbg(&sdev->udev->dev, "shutdown sockfd\n");
kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
}
diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c
index af10f7b..325b4c0 100644
--- a/drivers/usb/usbip/stub_main.c
+++ b/drivers/usb/usbip/stub_main.c
@@ -252,11 +252,12 @@
struct stub_priv *priv;
struct urb *urb;
- dev_dbg(&sdev->udev->dev, "free sdev %p\n", sdev);
+ dev_dbg(&sdev->udev->dev, "Stub device cleaning up urbs\n");
while ((priv = stub_priv_pop(sdev))) {
urb = priv->urb;
- dev_dbg(&sdev->udev->dev, "free urb %p\n", urb);
+ dev_dbg(&sdev->udev->dev, "free urb seqnum %lu\n",
+ priv->seqnum);
usb_kill_urb(urb);
kmem_cache_free(stub_priv_cache, priv);
diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c
index 283a9be..5b80718 100644
--- a/drivers/usb/usbip/stub_rx.c
+++ b/drivers/usb/usbip/stub_rx.c
@@ -225,9 +225,6 @@
if (priv->seqnum != pdu->u.cmd_unlink.seqnum)
continue;
- dev_info(&priv->urb->dev->dev, "unlink urb %p\n",
- priv->urb);
-
/*
* This matched urb is not completed yet (i.e., be in
* flight in usb hcd hardware/driver). Now we are
@@ -266,8 +263,8 @@
ret = usb_unlink_urb(priv->urb);
if (ret != -EINPROGRESS)
dev_err(&priv->urb->dev->dev,
- "failed to unlink a urb %p, ret %d\n",
- priv->urb, ret);
+ "failed to unlink a urb # %lu, ret %d\n",
+ priv->seqnum, ret);
return 0;
}
diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c
index 87ff94b..96aa375 100644
--- a/drivers/usb/usbip/stub_tx.c
+++ b/drivers/usb/usbip/stub_tx.c
@@ -102,7 +102,7 @@
/* link a urb to the queue of tx. */
spin_lock_irqsave(&sdev->priv_lock, flags);
if (sdev->ud.tcp_socket == NULL) {
- usbip_dbg_stub_tx("ignore urb for closed connection %p", urb);
+ usbip_dbg_stub_tx("ignore urb for closed connection\n");
/* It will be freed in stub_device_cleanup_urbs(). */
} else if (priv->unlinking) {
stub_enqueue_ret_unlink(sdev, priv->seqnum, urb->status);
@@ -204,8 +204,8 @@
/* 1. setup usbip_header */
setup_ret_submit_pdu(&pdu_header, urb);
- usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n",
- pdu_header.base.seqnum, urb);
+ usbip_dbg_stub_tx("setup txdata seqnum: %d\n",
+ pdu_header.base.seqnum);
usbip_header_correct_endian(&pdu_header, 1);
iov[iovnum].iov_base = &pdu_header;
diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c
index 8b23229..2a5d318 100644
--- a/drivers/usb/usbip/usbip_common.c
+++ b/drivers/usb/usbip/usbip_common.c
@@ -105,7 +105,7 @@
dev_dbg(dev, " devnum(%d) devpath(%s) usb speed(%s)",
udev->devnum, udev->devpath, usb_speed_string(udev->speed));
- pr_debug("tt %p, ttport %d\n", udev->tt, udev->ttport);
+ pr_debug("tt hub ttport %d\n", udev->ttport);
dev_dbg(dev, " ");
for (i = 0; i < 16; i++)
@@ -138,12 +138,8 @@
}
pr_debug("\n");
- dev_dbg(dev, "parent %p, bus %p\n", udev->parent, udev->bus);
-
- dev_dbg(dev,
- "descriptor %p, config %p, actconfig %p, rawdescriptors %p\n",
- &udev->descriptor, udev->config,
- udev->actconfig, udev->rawdescriptors);
+ dev_dbg(dev, "parent %s, bus %s\n", dev_name(&udev->parent->dev),
+ udev->bus->bus_name);
dev_dbg(dev, "have_langid %d, string_langid %d\n",
udev->have_langid, udev->string_langid);
@@ -251,9 +247,6 @@
dev = &urb->dev->dev;
- dev_dbg(dev, " urb :%p\n", urb);
- dev_dbg(dev, " dev :%p\n", urb->dev);
-
usbip_dump_usb_device(urb->dev);
dev_dbg(dev, " pipe :%08x ", urb->pipe);
@@ -262,11 +255,9 @@
dev_dbg(dev, " status :%d\n", urb->status);
dev_dbg(dev, " transfer_flags :%08X\n", urb->transfer_flags);
- dev_dbg(dev, " transfer_buffer :%p\n", urb->transfer_buffer);
dev_dbg(dev, " transfer_buffer_length:%d\n",
urb->transfer_buffer_length);
dev_dbg(dev, " actual_length :%d\n", urb->actual_length);
- dev_dbg(dev, " setup_packet :%p\n", urb->setup_packet);
if (urb->setup_packet && usb_pipetype(urb->pipe) == PIPE_CONTROL)
usbip_dump_usb_ctrlrequest(
@@ -276,8 +267,6 @@
dev_dbg(dev, " number_of_packets :%d\n", urb->number_of_packets);
dev_dbg(dev, " interval :%d\n", urb->interval);
dev_dbg(dev, " error_count :%d\n", urb->error_count);
- dev_dbg(dev, " context :%p\n", urb->context);
- dev_dbg(dev, " complete :%p\n", urb->complete);
}
EXPORT_SYMBOL_GPL(usbip_dump_urb);
@@ -335,13 +324,10 @@
char *bp = buf;
int osize = size;
- usbip_dbg_xmit("enter\n");
-
- if (!sock || !buf || !size) {
- pr_err("invalid arg, sock %p buff %p size %d\n", sock, buf,
- size);
+ if (!sock || !buf || !size)
return -EINVAL;
- }
+
+ usbip_dbg_xmit("enter\n");
do {
sock->sk->sk_allocation = GFP_NOIO;
@@ -354,11 +340,8 @@
msg.msg_flags = MSG_NOSIGNAL;
result = kernel_recvmsg(sock, &msg, &iov, 1, size, MSG_WAITALL);
- if (result <= 0) {
- pr_debug("receive sock %p buf %p size %u ret %d total %d\n",
- sock, buf, size, result, total);
+ if (result <= 0)
goto err;
- }
size -= result;
buf += result;
diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c
index d6dc165..7f161b0 100644
--- a/drivers/usb/usbip/vhci_hcd.c
+++ b/drivers/usb/usbip/vhci_hcd.c
@@ -506,9 +506,6 @@
struct vhci_device *vdev;
unsigned long flags;
- usbip_dbg_vhci_hc("enter, usb_hcd %p urb %p mem_flags %d\n",
- hcd, urb, mem_flags);
-
if (portnum > VHCI_HC_PORTS) {
pr_err("invalid port number %d\n", portnum);
return -ENODEV;
@@ -671,8 +668,6 @@
struct vhci_device *vdev;
unsigned long flags;
- pr_info("dequeue a urb %p\n", urb);
-
spin_lock_irqsave(&vhci->lock, flags);
priv = urb->hcpriv;
@@ -700,7 +695,6 @@
/* tcp connection is closed */
spin_lock(&vdev->priv_lock);
- pr_info("device %p seems to be disconnected\n", vdev);
list_del(&priv->list);
kfree(priv);
urb->hcpriv = NULL;
@@ -712,8 +706,6 @@
* vhci_rx will receive RET_UNLINK and give back the URB.
* Otherwise, we give back it here.
*/
- pr_info("gives back urb %p\n", urb);
-
usb_hcd_unlink_urb_from_ep(hcd, urb);
spin_unlock_irqrestore(&vhci->lock, flags);
@@ -741,8 +733,6 @@
unlink->unlink_seqnum = priv->seqnum;
- pr_info("device %p seems to be still connected\n", vdev);
-
/* send cmd_unlink and try to cancel the pending URB in the
* peer */
list_add_tail(&unlink->list, &vdev->unlink_tx);
@@ -823,7 +813,7 @@
/* need this? see stub_dev.c */
if (ud->tcp_socket) {
- pr_debug("shutdown tcp_socket %p\n", ud->tcp_socket);
+ pr_debug("shutdown tcp_socket\n");
kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
}
diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c
index fc2d319..5943dee 100644
--- a/drivers/usb/usbip/vhci_rx.c
+++ b/drivers/usb/usbip/vhci_rx.c
@@ -37,24 +37,23 @@
urb = priv->urb;
status = urb->status;
- usbip_dbg_vhci_rx("find urb %p vurb %p seqnum %u\n",
- urb, priv, seqnum);
+ usbip_dbg_vhci_rx("find urb seqnum %u\n", seqnum);
switch (status) {
case -ENOENT:
/* fall through */
case -ECONNRESET:
- dev_info(&urb->dev->dev,
- "urb %p was unlinked %ssynchronuously.\n", urb,
- status == -ENOENT ? "" : "a");
+ dev_dbg(&urb->dev->dev,
+ "urb seq# %u was unlinked %ssynchronuously\n",
+ seqnum, status == -ENOENT ? "" : "a");
break;
case -EINPROGRESS:
/* no info output */
break;
default:
- dev_info(&urb->dev->dev,
- "urb %p may be in a error, status %d\n", urb,
- status);
+ dev_dbg(&urb->dev->dev,
+ "urb seq# %u may be in a error, status %d\n",
+ seqnum, status);
}
list_del(&priv->list);
@@ -80,8 +79,8 @@
spin_unlock_irqrestore(&vdev->priv_lock, flags);
if (!urb) {
- pr_err("cannot find a urb of seqnum %u\n", pdu->base.seqnum);
- pr_info("max seqnum %d\n",
+ pr_err("cannot find a urb of seqnum %u max seqnum %d\n",
+ pdu->base.seqnum,
atomic_read(&vhci->seqnum));
usbip_event_add(ud, VDEV_EVENT_ERROR_TCP);
return;
@@ -104,7 +103,7 @@
if (usbip_dbg_flag_vhci_rx)
usbip_dump_urb(urb);
- usbip_dbg_vhci_rx("now giveback urb %p\n", urb);
+ usbip_dbg_vhci_rx("now giveback urb %u\n", pdu->base.seqnum);
spin_lock_irqsave(&vhci->lock, flags);
usb_hcd_unlink_urb_from_ep(vhci_to_hcd(vhci), urb);
@@ -170,7 +169,7 @@
pr_info("the urb (seqnum %d) was already given back\n",
pdu->base.seqnum);
} else {
- usbip_dbg_vhci_rx("now giveback urb %p\n", urb);
+ usbip_dbg_vhci_rx("now giveback urb %d\n", pdu->base.seqnum);
/* If unlink is successful, status is -ECONNRESET */
urb->status = pdu->u.ret_unlink.status;
diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c
index 3e7878f..a9a663a 100644
--- a/drivers/usb/usbip/vhci_tx.c
+++ b/drivers/usb/usbip/vhci_tx.c
@@ -83,7 +83,8 @@
memset(&msg, 0, sizeof(msg));
memset(&iov, 0, sizeof(iov));
- usbip_dbg_vhci_tx("setup txdata urb %p\n", urb);
+ usbip_dbg_vhci_tx("setup txdata urb seqnum %lu\n",
+ priv->seqnum);
/* 1. setup usbip_header */
setup_cmd_submit_pdu(&pdu_header, urb);
diff --git a/drivers/usb/usbip/vudc_rx.c b/drivers/usb/usbip/vudc_rx.c
index e429b59..d020e72 100644
--- a/drivers/usb/usbip/vudc_rx.c
+++ b/drivers/usb/usbip/vudc_rx.c
@@ -132,6 +132,25 @@
urb_p->new = 1;
urb_p->seqnum = pdu->base.seqnum;
+ if (urb_p->ep->type == USB_ENDPOINT_XFER_ISOC) {
+ /* validate packet size and number of packets */
+ unsigned int maxp, packets, bytes;
+
+ maxp = usb_endpoint_maxp(urb_p->ep->desc);
+ maxp *= usb_endpoint_maxp_mult(urb_p->ep->desc);
+ bytes = pdu->u.cmd_submit.transfer_buffer_length;
+ packets = DIV_ROUND_UP(bytes, maxp);
+
+ if (pdu->u.cmd_submit.number_of_packets < 0 ||
+ pdu->u.cmd_submit.number_of_packets > packets) {
+ dev_err(&udc->gadget.dev,
+ "CMD_SUBMIT: isoc invalid num packets %d\n",
+ pdu->u.cmd_submit.number_of_packets);
+ ret = -EMSGSIZE;
+ goto free_urbp;
+ }
+ }
+
ret = alloc_urb_from_cmd(&urb_p->urb, pdu, urb_p->ep->type);
if (ret) {
usbip_event_add(&udc->ud, VUDC_EVENT_ERROR_MALLOC);
diff --git a/drivers/usb/usbip/vudc_tx.c b/drivers/usb/usbip/vudc_tx.c
index 2346617..3ab4c86 100644
--- a/drivers/usb/usbip/vudc_tx.c
+++ b/drivers/usb/usbip/vudc_tx.c
@@ -97,6 +97,13 @@
memset(&pdu_header, 0, sizeof(pdu_header));
memset(&msg, 0, sizeof(msg));
+ if (urb->actual_length > 0 && !urb->transfer_buffer) {
+ dev_err(&udc->gadget.dev,
+ "urb: actual_length %d transfer_buffer null\n",
+ urb->actual_length);
+ return -1;
+ }
+
if (urb_p->type == USB_ENDPOINT_XFER_ISOC)
iovnum = 2 + urb->number_of_packets;
else
@@ -112,8 +119,8 @@
/* 1. setup usbip_header */
setup_ret_submit_pdu(&pdu_header, urb_p);
- usbip_dbg_stub_tx("setup txdata seqnum: %d urb: %p\n",
- pdu_header.base.seqnum, urb);
+ usbip_dbg_stub_tx("setup txdata seqnum: %d\n",
+ pdu_header.base.seqnum);
usbip_header_correct_endian(&pdu_header, 1);
iov[iovnum].iov_base = &pdu_header;
diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c
index 62469c6..75f942a 100644
--- a/fs/nfsd/auth.c
+++ b/fs/nfsd/auth.c
@@ -59,6 +59,9 @@
gi->gid[i] = exp->ex_anon_gid;
else
gi->gid[i] = rqgi->gid[i];
+
+ /* Each thread allocates its own gi, no race */
+ groups_sort(gi);
}
} else {
gi = get_group_info(rqgi);
diff --git a/fs/proc/array.c b/fs/proc/array.c
index c932ec4..794b52a 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -423,8 +423,11 @@
* safe because the task has stopped executing permanently.
*/
if (permitted && (task->flags & PF_DUMPCORE)) {
- eip = KSTK_EIP(task);
- esp = KSTK_ESP(task);
+ if (try_get_task_stack(task)) {
+ eip = KSTK_EIP(task);
+ esp = KSTK_ESP(task);
+ put_task_stack(task);
+ }
}
}
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index dc81e52..2e6000a 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -778,7 +778,14 @@
*/
#define PERCPU_INPUT(cacheline) \
VMLINUX_SYMBOL(__per_cpu_start) = .; \
+ VMLINUX_SYMBOL(__per_cpu_user_mapped_start) = .; \
*(.data..percpu..first) \
+ . = ALIGN(cacheline); \
+ *(.data..percpu..user_mapped) \
+ *(.data..percpu..user_mapped..shared_aligned) \
+ . = ALIGN(PAGE_SIZE); \
+ *(.data..percpu..user_mapped..page_aligned) \
+ VMLINUX_SYMBOL(__per_cpu_user_mapped_end) = .; \
. = ALIGN(PAGE_SIZE); \
*(.data..percpu..page_aligned) \
. = ALIGN(cacheline); \
diff --git a/include/dt-bindings/arm/arm-smmu.h b/include/dt-bindings/arm/arm-smmu.h
new file mode 100644
index 0000000..3a1dbd3
--- /dev/null
+++ b/include/dt-bindings/arm/arm-smmu.h
@@ -0,0 +1,27 @@
+/* Copyright (c) 2018, 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 __DT_ARM_SMMU_H__
+#define __DT_ARM_SMMU_H__
+
+#define ARM_SMMU_OPT_SECURE_CFG_ACCESS (1 << 0)
+#define ARM_SMMU_OPT_FATAL_ASF (1 << 1)
+#define ARM_SMMU_OPT_SKIP_INIT (1 << 2)
+#define ARM_SMMU_OPT_DYNAMIC (1 << 3)
+#define ARM_SMMU_OPT_3LVL_TABLES (1 << 4)
+#define ARM_SMMU_OPT_NO_ASID_RETENTION (1 << 5)
+#define ARM_SMMU_OPT_DISABLE_ATOS (1 << 6)
+#define ARM_SMMU_OPT_MMU500_ERRATA1 (1 << 7)
+#define ARM_SMMU_OPT_STATIC_CB (1 << 8)
+#define ARM_SMMU_OPT_HALT (1 << 9)
+
+#endif
diff --git a/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h b/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h
index 7e1394c..36d34b1 100644
--- a/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h
+++ b/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2018, 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
@@ -62,47 +62,44 @@
#define GCC_GP2_CLK_SRC 44
#define GCC_GP3_CLK 45
#define GCC_GP3_CLK_SRC 46
-#define GCC_MSS_CFG_AHB_CLK 47
-#define GCC_MSS_GPLL0_DIV_CLK_SRC 48
-#define GCC_MSS_SNOC_AXI_CLK 49
-#define GCC_PCIE_0_CLKREF_CLK 50
-#define GCC_PCIE_AUX_CLK 51
-#define GCC_PCIE_AUX_PHY_CLK_SRC 52
-#define GCC_PCIE_CFG_AHB_CLK 53
-#define GCC_PCIE_MSTR_AXI_CLK 54
-#define GCC_PCIE_PHY_REFGEN_CLK 55
-#define GCC_PCIE_PHY_REFGEN_CLK_SRC 56
-#define GCC_PCIE_PIPE_CLK 57
-#define GCC_PCIE_SLEEP_CLK 58
-#define GCC_PCIE_SLV_AXI_CLK 59
-#define GCC_PCIE_SLV_Q2A_AXI_CLK 60
-#define GCC_PDM2_CLK 61
-#define GCC_PDM2_CLK_SRC 62
-#define GCC_PDM_AHB_CLK 63
-#define GCC_PDM_XO4_CLK 64
-#define GCC_PRNG_AHB_CLK 65
-#define GCC_SDCC1_AHB_CLK 66
-#define GCC_SDCC1_APPS_CLK 67
-#define GCC_SDCC1_APPS_CLK_SRC 68
-#define GCC_SPMI_FETCHER_AHB_CLK 69
-#define GCC_SPMI_FETCHER_CLK 70
-#define GCC_SPMI_FETCHER_CLK_SRC 71
-#define GCC_SYS_NOC_CPUSS_AHB_CLK 72
-#define GCC_SYS_NOC_USB3_CLK 73
-#define GCC_USB30_MASTER_CLK 74
-#define GCC_USB30_MASTER_CLK_SRC 75
-#define GCC_USB30_MOCK_UTMI_CLK 76
-#define GCC_USB30_MOCK_UTMI_CLK_SRC 77
-#define GCC_USB30_SLEEP_CLK 78
-#define GCC_USB3_PHY_AUX_CLK 79
-#define GCC_USB3_PHY_AUX_CLK_SRC 80
-#define GCC_USB3_PHY_PIPE_CLK 81
-#define GCC_USB3_PRIM_CLKREF_CLK 82
-#define GCC_USB_PHY_CFG_AHB2PHY_CLK 83
-#define GPLL0 84
-#define GPLL0_OUT_EVEN 85
-#define GPLL4 86
-#define GPLL4_OUT_EVEN 87
+#define GCC_PCIE_0_CLKREF_CLK 47
+#define GCC_PCIE_AUX_CLK 48
+#define GCC_PCIE_AUX_PHY_CLK_SRC 49
+#define GCC_PCIE_CFG_AHB_CLK 50
+#define GCC_PCIE_MSTR_AXI_CLK 51
+#define GCC_PCIE_PHY_REFGEN_CLK 52
+#define GCC_PCIE_PHY_REFGEN_CLK_SRC 53
+#define GCC_PCIE_PIPE_CLK 54
+#define GCC_PCIE_SLEEP_CLK 55
+#define GCC_PCIE_SLV_AXI_CLK 56
+#define GCC_PCIE_SLV_Q2A_AXI_CLK 57
+#define GCC_PDM2_CLK 58
+#define GCC_PDM2_CLK_SRC 59
+#define GCC_PDM_AHB_CLK 60
+#define GCC_PDM_XO4_CLK 61
+#define GCC_PRNG_AHB_CLK 62
+#define GCC_SDCC1_AHB_CLK 63
+#define GCC_SDCC1_APPS_CLK 64
+#define GCC_SDCC1_APPS_CLK_SRC 65
+#define GCC_SPMI_FETCHER_AHB_CLK 66
+#define GCC_SPMI_FETCHER_CLK 67
+#define GCC_SPMI_FETCHER_CLK_SRC 68
+#define GCC_SYS_NOC_CPUSS_AHB_CLK 69
+#define GCC_SYS_NOC_USB3_CLK 70
+#define GCC_USB30_MASTER_CLK 71
+#define GCC_USB30_MASTER_CLK_SRC 72
+#define GCC_USB30_MOCK_UTMI_CLK 73
+#define GCC_USB30_MOCK_UTMI_CLK_SRC 74
+#define GCC_USB30_SLEEP_CLK 75
+#define GCC_USB3_PHY_AUX_CLK 76
+#define GCC_USB3_PHY_AUX_CLK_SRC 77
+#define GCC_USB3_PHY_PIPE_CLK 78
+#define GCC_USB3_PRIM_CLKREF_CLK 79
+#define GCC_USB_PHY_CFG_AHB2PHY_CLK 80
+#define GPLL0 81
+#define GPLL0_OUT_EVEN 82
+#define GPLL4 83
+#define GPLL4_OUT_EVEN 84
/* CPU clocks */
#define CLOCK_A7SS 0
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 97498be..75ffd3b 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -43,6 +43,7 @@
u32 max_entries;
u32 map_flags;
u32 pages;
+ bool unpriv_array;
struct user_struct *user;
const struct bpf_map_ops *ops;
struct work_struct work;
@@ -189,6 +190,7 @@
struct bpf_array {
struct bpf_map map;
u32 elem_size;
+ u32 index_mask;
/* 'ownership' of prog_array is claimed by the first program that
* is going to use this map or by the first program which FD is stored
* in the map to make sure that all callers and callees have the same
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 4c4e935..070fc49 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -67,7 +67,10 @@
};
struct bpf_insn_aux_data {
- enum bpf_reg_type ptr_type; /* pointer type for load/store insns */
+ union {
+ enum bpf_reg_type ptr_type; /* pointer type for load/store insns */
+ struct bpf_map *map_ptr; /* pointer for call insn into lookup_elem */
+ };
bool seen; /* this insn was processed by the verifier */
};
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 3484287..1f7e4ec 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -44,6 +44,13 @@
extern int cpu_add_dev_attr_group(struct attribute_group *attrs);
extern void cpu_remove_dev_attr_group(struct attribute_group *attrs);
+extern ssize_t cpu_show_meltdown(struct device *dev,
+ struct device_attribute *attr, char *buf);
+extern ssize_t cpu_show_spectre_v1(struct device *dev,
+ struct device_attribute *attr, char *buf);
+extern ssize_t cpu_show_spectre_v2(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
extern __printf(4, 5)
struct device *cpu_device_create(struct device *parent, void *drvdata,
const struct attribute_group **groups,
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index d921206..6500554 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -51,7 +51,7 @@
CPUHP_SH_SH3X_PREPARE,
CPUHP_BLK_MQ_PREPARE,
CPUHP_TOPOLOGY_PREPARE,
- CPUHP_TIMERS_DEAD,
+ CPUHP_TIMERS_PREPARE,
CPUHP_NOTF_ERR_INJ_PREPARE,
CPUHP_MIPS_SOC_PREPARE,
CPUHP_BRINGUP_CPU,
diff --git a/include/linux/cred.h b/include/linux/cred.h
index f0e70a1..cf1a5d0 100644
--- a/include/linux/cred.h
+++ b/include/linux/cred.h
@@ -82,6 +82,7 @@
extern void set_groups(struct cred *, struct group_info *);
extern int groups_search(const struct group_info *, kgid_t);
extern bool may_setgroups(void);
+extern void groups_sort(struct group_info *);
/*
* The security context of a task
diff --git a/include/linux/frame.h b/include/linux/frame.h
index e6baaba..d772c61 100644
--- a/include/linux/frame.h
+++ b/include/linux/frame.h
@@ -11,7 +11,7 @@
* For more information, see tools/objtool/Documentation/stack-validation.txt.
*/
#define STACK_FRAME_NON_STANDARD(func) \
- static void __used __section(__func_stack_frame_non_standard) \
+ static void __used __section(.discard.func_stack_frame_non_standard) \
*__func_stack_frame_non_standard_##func = func
#else /* !CONFIG_STACK_VALIDATION */
diff --git a/include/linux/fscache.h b/include/linux/fscache.h
index 115bb81..94a8aae 100644
--- a/include/linux/fscache.h
+++ b/include/linux/fscache.h
@@ -764,7 +764,7 @@
{
if (fscache_cookie_valid(cookie) && PageFsCache(page))
return __fscache_maybe_release_page(cookie, page, gfp);
- return false;
+ return true;
}
/**
diff --git a/include/linux/input/synaptics_dsx_v2_6.h b/include/linux/input/synaptics_dsx_v2_6.h
index 7deef35..2b91bc0 100644
--- a/include/linux/input/synaptics_dsx_v2_6.h
+++ b/include/linux/input/synaptics_dsx_v2_6.h
@@ -34,13 +34,13 @@
#ifndef _SYNAPTICS_DSX_H_
#define _SYNAPTICS_DSX_H_
-#define PLATFORM_DRIVER_NAME "synaptics_dsx"
-#define STYLUS_DRIVER_NAME "synaptics_dsx_stylus"
-#define ACTIVE_PEN_DRIVER_NAME "synaptics_dsx_active_pen"
-#define PROXIMITY_DRIVER_NAME "synaptics_dsx_proximity"
-#define GESTURE_DRIVER_NAME "synaptics_dsx_gesture"
-#define I2C_DRIVER_NAME "synaptics_dsx_i2c"
-#define SPI_DRIVER_NAME "synaptics_dsx_spi"
+#define PLATFORM_DRIVER_NAME "synaptics_dsxv26"
+#define STYLUS_DRIVER_NAME "synaptics_dsxv26_stylus"
+#define ACTIVE_PEN_DRIVER_NAME "synaptics_dsxv26_active_pen"
+#define PROXIMITY_DRIVER_NAME "synaptics_dsxv26_proximity"
+#define GESTURE_DRIVER_NAME "synaptics_dsxv26_gesture"
+#define I2C_DRIVER_NAME "synaptics_dsxv26"
+#define SPI_DRIVER_NAME "synaptics_dsxv26"
/*
* struct synaptics_dsx_button_map - button map
diff --git a/include/linux/ipa_usb.h b/include/linux/ipa_usb.h
index de11633..cceae83 100644
--- a/include/linux/ipa_usb.h
+++ b/include/linux/ipa_usb.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, 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
@@ -121,14 +121,16 @@
* @dir: channel direction
* @xfer_ring_len: length of transfer ring in bytes (must be integral
* multiple of transfer element size - 16B for xDCI)
- * @xfer_ring_base_addr: physical base address of transfer ring. Address must be
- * aligned to xfer_ring_len rounded to power of two
* @xfer_scratch: parameters for xDCI channel scratch
- * @xfer_ring_base_addr_iova: IO virtual address mapped to xfer_ring_base_addr
+ * @xfer_ring_base_addr_iova: IO virtual address mapped to pysical base address
* @data_buff_base_len: length of data buffer allocated by USB driver
- * @data_buff_base_addr: physical base address for the data buffer (where TRBs
- * points)
- * @data_buff_base_addr_iova: IO virtual address mapped to data_buff_base_addr
+ * @data_buff_base_addr_iova: IO virtual address mapped to pysical base address
+ * @sgt_xfer_rings: Scatter table for Xfer rings,contains valid non NULL
+ * value
+ * when USB S1-SMMU enabed, else NULL.
+ * @sgt_data_buff: Scatter table for data buffs,contains valid non NULL
+ * value
+ * when USB S1-SMMU enabed, else NULL.
*
*/
struct ipa_usb_xdci_chan_params {
@@ -143,12 +145,12 @@
/* transfer ring params */
enum gsi_chan_dir dir;
u16 xfer_ring_len;
- u64 xfer_ring_base_addr;
struct ipa_usb_xdci_chan_scratch xfer_scratch;
u64 xfer_ring_base_addr_iova;
u32 data_buff_base_len;
- u64 data_buff_base_addr;
u64 data_buff_base_addr_iova;
+ struct sg_table *sgt_xfer_rings;
+ struct sg_table *sgt_data_buff;
};
/**
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 3b94400..11ff751 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -248,7 +248,8 @@
* 100: prefer care-of address
*/
dontfrag:1,
- autoflowlabel:1;
+ autoflowlabel:1,
+ autoflowlabel_set:1;
__u8 min_hopcount;
__u8 tclass;
__be32 rcv_flowinfo;
diff --git a/include/linux/kaiser.h b/include/linux/kaiser.h
new file mode 100644
index 0000000..58c55b1
--- /dev/null
+++ b/include/linux/kaiser.h
@@ -0,0 +1,52 @@
+#ifndef _LINUX_KAISER_H
+#define _LINUX_KAISER_H
+
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+#include <asm/kaiser.h>
+
+static inline int kaiser_map_thread_stack(void *stack)
+{
+ /*
+ * Map that page of kernel stack on which we enter from user context.
+ */
+ return kaiser_add_mapping((unsigned long)stack +
+ THREAD_SIZE - PAGE_SIZE, PAGE_SIZE, __PAGE_KERNEL);
+}
+
+static inline void kaiser_unmap_thread_stack(void *stack)
+{
+ /*
+ * Note: may be called even when kaiser_map_thread_stack() failed.
+ */
+ kaiser_remove_mapping((unsigned long)stack +
+ THREAD_SIZE - PAGE_SIZE, PAGE_SIZE);
+}
+#else
+
+/*
+ * These stubs are used whenever CONFIG_PAGE_TABLE_ISOLATION is off, which
+ * includes architectures that support KAISER, but have it disabled.
+ */
+
+static inline void kaiser_init(void)
+{
+}
+static inline int kaiser_add_mapping(unsigned long addr,
+ unsigned long size, unsigned long flags)
+{
+ return 0;
+}
+static inline void kaiser_remove_mapping(unsigned long start,
+ unsigned long size)
+{
+}
+static inline int kaiser_map_thread_stack(void *stack)
+{
+ return 0;
+}
+static inline void kaiser_unmap_thread_stack(void *stack)
+{
+}
+
+#endif /* !CONFIG_PAGE_TABLE_ISOLATION */
+#endif /* _LINUX_KAISER_H */
diff --git a/include/linux/leds-qpnp-flash.h b/include/linux/leds-qpnp-flash.h
index 1fe6e17..e3b9cf1 100644
--- a/include/linux/leds-qpnp-flash.h
+++ b/include/linux/leds-qpnp-flash.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018, 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
@@ -21,7 +21,14 @@
#define FLASH_LED_PREPARE_OPTIONS_MASK GENMASK(3, 0)
-int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
+#if (defined CONFIG_LEDS_QPNP_FLASH || defined CONFIG_LEDS_QPNP_FLASH_V2)
+extern int (*qpnp_flash_led_prepare)(struct led_trigger *trig, int options,
int *max_current);
-
+#else
+static inline int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
+ int *max_current)
+{
+ return -ENODEV;
+}
+#endif
#endif
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 8b35bdb..fd77f83 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -490,9 +490,21 @@
extern int do_swap_account;
#endif
-void lock_page_memcg(struct page *page);
+struct mem_cgroup *lock_page_memcg(struct page *page);
+void __unlock_page_memcg(struct mem_cgroup *memcg);
void unlock_page_memcg(struct page *page);
+static inline void __mem_cgroup_update_page_stat(struct page *page,
+ struct mem_cgroup *memcg,
+ enum mem_cgroup_stat_index idx,
+ int val)
+{
+ VM_BUG_ON(!(rcu_read_lock_held() || PageLocked(page)));
+
+ if (memcg && memcg->stat)
+ this_cpu_add(memcg->stat->count[idx], val);
+}
+
/**
* mem_cgroup_update_page_stat - update page state statistics
* @page: the page
@@ -508,13 +520,12 @@
* mem_cgroup_update_page_stat(page, state, -1);
* unlock_page(page) or unlock_page_memcg(page)
*/
+
static inline void mem_cgroup_update_page_stat(struct page *page,
enum mem_cgroup_stat_index idx, int val)
{
- VM_BUG_ON(!(rcu_read_lock_held() || PageLocked(page)));
- if (page->mem_cgroup)
- this_cpu_add(page->mem_cgroup->stat->count[idx], val);
+ __mem_cgroup_update_page_stat(page, page->mem_cgroup, idx, val);
}
static inline void mem_cgroup_inc_page_stat(struct page *page,
@@ -709,7 +720,12 @@
{
}
-static inline void lock_page_memcg(struct page *page)
+static inline struct mem_cgroup *lock_page_memcg(struct page *page)
+{
+ return NULL;
+}
+
+static inline void __unlock_page_memcg(struct mem_cgroup *memcg)
{
}
@@ -745,6 +761,13 @@
{
}
+static inline void __mem_cgroup_update_page_stat(struct page *page,
+ struct mem_cgroup *memcg,
+ enum mem_cgroup_stat_index idx,
+ int nr)
+{
+}
+
static inline void mem_cgroup_inc_page_stat(struct page *page,
enum mem_cgroup_stat_index idx)
{
diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h
index 6045d4d..25ed105 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -143,7 +143,7 @@
MLX5_CMD_OP_ALLOC_Q_COUNTER = 0x771,
MLX5_CMD_OP_DEALLOC_Q_COUNTER = 0x772,
MLX5_CMD_OP_QUERY_Q_COUNTER = 0x773,
- MLX5_CMD_OP_SET_RATE_LIMIT = 0x780,
+ MLX5_CMD_OP_SET_PP_RATE_LIMIT = 0x780,
MLX5_CMD_OP_QUERY_RATE_LIMIT = 0x781,
MLX5_CMD_OP_ALLOC_PD = 0x800,
MLX5_CMD_OP_DEALLOC_PD = 0x801,
@@ -6689,7 +6689,7 @@
u8 vxlan_udp_port[0x10];
};
-struct mlx5_ifc_set_rate_limit_out_bits {
+struct mlx5_ifc_set_pp_rate_limit_out_bits {
u8 status[0x8];
u8 reserved_at_8[0x18];
@@ -6698,7 +6698,7 @@
u8 reserved_at_40[0x40];
};
-struct mlx5_ifc_set_rate_limit_in_bits {
+struct mlx5_ifc_set_pp_rate_limit_in_bits {
u8 opcode[0x10];
u8 reserved_at_10[0x10];
@@ -6711,6 +6711,8 @@
u8 reserved_at_60[0x20];
u8 rate_limit[0x20];
+
+ u8 reserved_at_a0[0x160];
};
struct mlx5_ifc_access_register_out_bits {
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index 90900c2..0c28c28 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -133,8 +133,9 @@
NR_SLAB_UNRECLAIMABLE,
NR_PAGETABLE, /* used for pagetables */
NR_KERNEL_STACK_KB, /* measured in KiB */
- /* Second 128 byte cacheline */
+ NR_KAISERTABLE,
NR_BOUNCE,
+ /* Second 128 byte cacheline */
#if IS_ENABLED(CONFIG_ZSMALLOC)
NR_ZSPAGES, /* allocated in zsmalloc */
#endif
diff --git a/include/linux/percpu-defs.h b/include/linux/percpu-defs.h
index 8f16299..8902f23 100644
--- a/include/linux/percpu-defs.h
+++ b/include/linux/percpu-defs.h
@@ -35,6 +35,12 @@
#endif
+#ifdef CONFIG_PAGE_TABLE_ISOLATION
+#define USER_MAPPED_SECTION "..user_mapped"
+#else
+#define USER_MAPPED_SECTION ""
+#endif
+
/*
* Base implementations of per-CPU variable declarations and definitions, where
* the section in which the variable is to be placed is provided by the
@@ -115,6 +121,12 @@
#define DEFINE_PER_CPU(type, name) \
DEFINE_PER_CPU_SECTION(type, name, "")
+#define DECLARE_PER_CPU_USER_MAPPED(type, name) \
+ DECLARE_PER_CPU_SECTION(type, name, USER_MAPPED_SECTION)
+
+#define DEFINE_PER_CPU_USER_MAPPED(type, name) \
+ DEFINE_PER_CPU_SECTION(type, name, USER_MAPPED_SECTION)
+
/*
* Declaration/definition used for per-CPU variables that must come first in
* the set of variables.
@@ -144,6 +156,14 @@
DEFINE_PER_CPU_SECTION(type, name, PER_CPU_SHARED_ALIGNED_SECTION) \
____cacheline_aligned_in_smp
+#define DECLARE_PER_CPU_SHARED_ALIGNED_USER_MAPPED(type, name) \
+ DECLARE_PER_CPU_SECTION(type, name, USER_MAPPED_SECTION PER_CPU_SHARED_ALIGNED_SECTION) \
+ ____cacheline_aligned_in_smp
+
+#define DEFINE_PER_CPU_SHARED_ALIGNED_USER_MAPPED(type, name) \
+ DEFINE_PER_CPU_SECTION(type, name, USER_MAPPED_SECTION PER_CPU_SHARED_ALIGNED_SECTION) \
+ ____cacheline_aligned_in_smp
+
#define DECLARE_PER_CPU_ALIGNED(type, name) \
DECLARE_PER_CPU_SECTION(type, name, PER_CPU_ALIGNED_SECTION) \
____cacheline_aligned
@@ -162,11 +182,21 @@
#define DEFINE_PER_CPU_PAGE_ALIGNED(type, name) \
DEFINE_PER_CPU_SECTION(type, name, "..page_aligned") \
__aligned(PAGE_SIZE)
+/*
+ * Declaration/definition used for per-CPU variables that must be page aligned and need to be mapped in user mode.
+ */
+#define DECLARE_PER_CPU_PAGE_ALIGNED_USER_MAPPED(type, name) \
+ DECLARE_PER_CPU_SECTION(type, name, USER_MAPPED_SECTION"..page_aligned") \
+ __aligned(PAGE_SIZE)
+
+#define DEFINE_PER_CPU_PAGE_ALIGNED_USER_MAPPED(type, name) \
+ DEFINE_PER_CPU_SECTION(type, name, USER_MAPPED_SECTION"..page_aligned") \
+ __aligned(PAGE_SIZE)
/*
* Declaration/definition used for per-CPU variables that must be read mostly.
*/
-#define DECLARE_PER_CPU_READ_MOSTLY(type, name) \
+#define DECLARE_PER_CPU_READ_MOSTLY(type, name) \
DECLARE_PER_CPU_SECTION(type, name, "..read_mostly")
#define DEFINE_PER_CPU_READ_MOSTLY(type, name) \
diff --git a/include/linux/phy.h b/include/linux/phy.h
index a04d69a..867110c 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -684,6 +684,17 @@
}
/**
+ * phy_interface_mode_is_rgmii - Convenience function for testing if a
+ * PHY interface mode is RGMII (all variants)
+ * @mode: the phy_interface_t enum
+ */
+static inline bool phy_interface_mode_is_rgmii(phy_interface_t mode)
+{
+ return mode >= PHY_INTERFACE_MODE_RGMII &&
+ mode <= PHY_INTERFACE_MODE_RGMII_TXID;
+};
+
+/**
* phy_interface_is_rgmii - Convenience function for testing if a PHY interface
* is RGMII (all variants)
* @phydev: the phy_device struct
diff --git a/include/linux/ptr_ring.h b/include/linux/ptr_ring.h
index b83507c..e38f471 100644
--- a/include/linux/ptr_ring.h
+++ b/include/linux/ptr_ring.h
@@ -99,12 +99,18 @@
/* Note: callers invoking this in a loop must use a compiler barrier,
* for example cpu_relax(). Callers must hold producer_lock.
+ * Callers are responsible for making sure pointer that is being queued
+ * points to a valid data.
*/
static inline int __ptr_ring_produce(struct ptr_ring *r, void *ptr)
{
if (unlikely(!r->size) || r->queue[r->producer])
return -ENOSPC;
+ /* Make sure the pointer we are storing points to a valid data. */
+ /* Pairs with smp_read_barrier_depends in __ptr_ring_consume. */
+ smp_wmb();
+
r->queue[r->producer++] = ptr;
if (unlikely(r->producer >= r->size))
r->producer = 0;
@@ -244,6 +250,9 @@
if (ptr)
__ptr_ring_discard_one(r);
+ /* Make sure anyone accessing data through the pointer is up to date. */
+ /* Pairs with smp_wmb in __ptr_ring_produce. */
+ smp_read_barrier_depends();
return ptr;
}
diff --git a/include/linux/sh_eth.h b/include/linux/sh_eth.h
index f2e27e0..01b3778 100644
--- a/include/linux/sh_eth.h
+++ b/include/linux/sh_eth.h
@@ -16,7 +16,6 @@
unsigned char mac_addr[ETH_ALEN];
unsigned no_ether_link:1;
unsigned ether_link_active_low:1;
- unsigned needs_init:1;
};
#endif
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index 647532b..f50b717c 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -219,7 +219,8 @@
} rack;
u16 advmss; /* Advertised MSS */
u8 rate_app_limited:1, /* rate_{delivered,interval_us} limited? */
- unused:7;
+ is_sack_reneg:1, /* in recovery from loss with SACK reneg? */
+ unused:6;
u8 nonagle : 4,/* Disable Nagle algorithm? */
thin_lto : 1,/* Use linear timeouts for thin streams */
thin_dupack : 1,/* Fast retransmit on first dupack */
diff --git a/include/linux/timer.h b/include/linux/timer.h
index 5f5107b..c2476e2 100644
--- a/include/linux/timer.h
+++ b/include/linux/timer.h
@@ -237,9 +237,11 @@
unsigned long round_jiffies_up_relative(unsigned long j);
#ifdef CONFIG_HOTPLUG_CPU
+int timers_prepare_cpu(unsigned int cpu);
int timers_dead_cpu(unsigned int cpu);
#else
-#define timers_dead_cpu NULL
+#define timers_prepare_cpu NULL
+#define timers_dead_cpu NULL
#endif
#endif
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 40144f3..a41244f 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -394,6 +394,8 @@
/* tty_io.c */
extern int __init tty_init(void);
extern const char *tty_name(const struct tty_struct *tty);
+extern int tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout);
+extern void tty_ldisc_unlock(struct tty_struct *tty);
#else
static inline void console_init(void)
{ }
diff --git a/include/linux/usb/usb_qdss.h b/include/linux/usb/usb_qdss.h
index b58d8ee..fe626c18 100644
--- a/include/linux/usb/usb_qdss.h
+++ b/include/linux/usb/usb_qdss.h
@@ -15,6 +15,9 @@
#include <linux/kernel.h>
+#define USB_QDSS_CH_MDM "qdss_mdm"
+#define USB_QDSS_CH_MSM "qdss"
+
struct qdss_request {
char *buf;
int length;
diff --git a/include/linux/vm_event_item.h b/include/linux/vm_event_item.h
index 9cc195f..a9c2e4c 100644
--- a/include/linux/vm_event_item.h
+++ b/include/linux/vm_event_item.h
@@ -89,10 +89,8 @@
#endif
#endif
#ifdef CONFIG_DEBUG_TLBFLUSH
-#ifdef CONFIG_SMP
NR_TLB_REMOTE_FLUSH, /* cpu tried to flush others' tlbs */
NR_TLB_REMOTE_FLUSH_RECEIVED,/* cpu received ipi for flush */
-#endif /* CONFIG_SMP */
NR_TLB_LOCAL_FLUSH_ALL,
NR_TLB_LOCAL_FLUSH_ONE,
#endif /* CONFIG_DEBUG_TLBFLUSH */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 63ce902..d5e79f1 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -55,6 +55,12 @@
*/
#define CFG80211_ROAMED_API_UNIFIED 1
+/* Indicate backport support for DBS scan control */
+#define CFG80211_SCAN_DBS_CONTROL_SUPPORT 1
+
+/* Indicate backport support for per chain rssi scan */
+#define CFG80211_SCAN_PER_CHAIN_RSSI_SUPPORT 1
+
/**
* DOC: Introduction
*
@@ -1742,6 +1748,8 @@
* by %parent_bssid.
* @parent_bssid: the BSS according to which %parent_tsf is set. This is set to
* the BSS that requested the scan in which the beacon/probe was received.
+ * @chains: bitmask for filled values in @chain_signal.
+ * @chain_signal: per-chain signal strength of last received BSS in dBm.
*/
struct cfg80211_inform_bss {
struct ieee80211_channel *chan;
@@ -1750,6 +1758,8 @@
u64 boottime_ns;
u64 parent_tsf;
u8 parent_bssid[ETH_ALEN] __aligned(2);
+ u8 chains;
+ s8 chain_signal[IEEE80211_MAX_CHAINS];
};
/**
@@ -1793,6 +1803,8 @@
* that holds the beacon data. @beacon_ies is still valid, of course, and
* points to the same data as hidden_beacon_bss->beacon_ies in that case.
* @signal: signal strength value (type depends on the wiphy's signal_type)
+ * @chains: bitmask for filled values in @chain_signal.
+ * @chain_signal: per-chain signal strength of last received BSS in dBm.
* @priv: private area for driver use, has at least wiphy->bss_priv_size bytes
*/
struct cfg80211_bss {
@@ -1811,6 +1823,8 @@
u16 capability;
u8 bssid[ETH_ALEN];
+ u8 chains;
+ s8 chain_signal[IEEE80211_MAX_CHAINS];
u8 priv[0] __aligned(sizeof(void *));
};
diff --git a/include/net/ip.h b/include/net/ip.h
index 4ef6792..ad065a4 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -33,6 +33,8 @@
#include <net/flow.h>
#include <net/flow_dissector.h>
+#define IPV4_MIN_MTU 68 /* RFC 791 */
+
struct sock;
struct inet_skb_parm {
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 2c7d876..8fd61bc 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1007,7 +1007,7 @@
* @RX_FLAG_DECRYPTED: This frame was decrypted in hardware.
* @RX_FLAG_MMIC_STRIPPED: the Michael MIC is stripped off this frame,
* verification has been done by the hardware.
- * @RX_FLAG_IV_STRIPPED: The IV/ICV are stripped from this frame.
+ * @RX_FLAG_IV_STRIPPED: The IV and ICV are stripped from this frame.
* If this flag is set, the stack cannot do any replay detection
* hence the driver or hardware will have to do that.
* @RX_FLAG_PN_VALIDATED: Currently only valid for CCMP/GCMP frames, this
@@ -1078,6 +1078,8 @@
* @RX_FLAG_ALLOW_SAME_PN: Allow the same PN as same packet before.
* This is used for AMSDU subframes which can have the same PN as
* the first subframe.
+ * @RX_FLAG_ICV_STRIPPED: The ICV is stripped from this frame. CRC checking must
+ * be done in the hardware.
*/
enum mac80211_rx_flags {
RX_FLAG_MMIC_ERROR = BIT(0),
@@ -1113,6 +1115,7 @@
RX_FLAG_RADIOTAP_VENDOR_DATA = BIT(31),
RX_FLAG_MIC_STRIPPED = BIT_ULL(32),
RX_FLAG_ALLOW_SAME_PN = BIT_ULL(33),
+ RX_FLAG_ICV_STRIPPED = BIT_ULL(34),
};
#define RX_FLAG_STBC_SHIFT 26
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 775c3bd..448aec0 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1017,7 +1017,7 @@
void tcp_rate_skb_delivered(struct sock *sk, struct sk_buff *skb,
struct rate_sample *rs);
void tcp_rate_gen(struct sock *sk, u32 delivered, u32 lost,
- struct skb_mstamp *now, struct rate_sample *rs);
+ bool is_sack_reneg, struct skb_mstamp *now, struct rate_sample *rs);
void tcp_rate_check_app_limited(struct sock *sk);
/* These functions determine how the current flow behaves in respect of SACK
diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h
index ad38816..31b4cce 100644
--- a/include/soc/qcom/icnss.h
+++ b/include/soc/qcom/icnss.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 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
@@ -23,7 +23,6 @@
#endif
enum icnss_uevent {
- ICNSS_UEVENT_FW_READY,
ICNSS_UEVENT_FW_CRASHED,
ICNSS_UEVENT_FW_DOWN,
};
diff --git a/include/soc/qcom/msm_tz_smmu.h b/include/soc/qcom/msm_tz_smmu.h
index 1d47f1f..43a3069 100644
--- a/include/soc/qcom/msm_tz_smmu.h
+++ b/include/soc/qcom/msm_tz_smmu.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 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
@@ -60,11 +60,16 @@
/* Donot write to smmu global space with CONFIG_MSM_TZ_SMMU */
#undef writel_relaxed
+#undef writeq_relaxed
#define writel_relaxed(v, c) do { \
if (!arm_smmu_skip_write(c)) \
((void)__raw_writel((__force u32)cpu_to_le32(v), (c))); \
} while (0)
+#define writeq_relaxed(v, c) do { \
+ if (!arm_smmu_skip_write(c)) \
+ ((void)__raw_writeq((__force u64)cpu_to_le64(v), (c))); \
+ } while (0)
#else
static inline int msm_tz_smmu_atos_start(struct device *dev, int cb_num)
diff --git a/include/sound/wcd-dsp-mgr.h b/include/sound/wcd-dsp-mgr.h
index 7ba1817..52b52c4 100644
--- a/include/sound/wcd-dsp-mgr.h
+++ b/include/sound/wcd-dsp-mgr.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2018, 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
@@ -80,6 +80,7 @@
/* Software generated signal indicating debug dumps to be collected */
WDSP_DEBUG_DUMP,
+ WDSP_DEBUG_DUMP_INTERNAL,
};
/*
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index eb3b23b..30f99ce 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -493,6 +493,7 @@
#define CMD_T_BUSY (1 << 9)
#define CMD_T_TAS (1 << 10)
#define CMD_T_FABRIC_STOP (1 << 11)
+#define CMD_T_PRE_EXECUTE (1 << 12)
spinlock_t t_state_lock;
struct kref cmd_kref;
struct completion t_transport_stop_comp;
diff --git a/include/trace/events/kvm.h b/include/trace/events/kvm.h
index 8ade3eb..90fce4d 100644
--- a/include/trace/events/kvm.h
+++ b/include/trace/events/kvm.h
@@ -208,7 +208,7 @@
{ KVM_TRACE_MMIO_WRITE, "write" }
TRACE_EVENT(kvm_mmio,
- TP_PROTO(int type, int len, u64 gpa, u64 val),
+ TP_PROTO(int type, int len, u64 gpa, void *val),
TP_ARGS(type, len, gpa, val),
TP_STRUCT__entry(
@@ -222,7 +222,10 @@
__entry->type = type;
__entry->len = len;
__entry->gpa = gpa;
- __entry->val = val;
+ __entry->val = 0;
+ if (val)
+ memcpy(&__entry->val, val,
+ min_t(u32, sizeof(__entry->val), len));
),
TP_printk("mmio %s len %u gpa 0x%llx val 0x%llx",
diff --git a/include/uapi/linux/eventpoll.h b/include/uapi/linux/eventpoll.h
index 81c464a..6016e9e 100644
--- a/include/uapi/linux/eventpoll.h
+++ b/include/uapi/linux/eventpoll.h
@@ -26,8 +26,21 @@
#define EPOLL_CTL_DEL 2
#define EPOLL_CTL_MOD 3
+/* Epoll event masks */
+#define EPOLLIN 0x00000001
+#define EPOLLPRI 0x00000002
+#define EPOLLOUT 0x00000004
+#define EPOLLERR 0x00000008
+#define EPOLLHUP 0x00000010
+#define EPOLLRDNORM 0x00000040
+#define EPOLLRDBAND 0x00000080
+#define EPOLLWRNORM 0x00000100
+#define EPOLLWRBAND 0x00000200
+#define EPOLLMSG 0x00000400
+#define EPOLLRDHUP 0x00002000
+
/* Set exclusive wakeup mode for the target file descriptor */
-#define EPOLLEXCLUSIVE (1 << 28)
+#define EPOLLEXCLUSIVE (1U << 28)
/*
* Request the handling of system wakeup events so as to prevent system suspends
@@ -39,13 +52,13 @@
*
* Requires CAP_BLOCK_SUSPEND
*/
-#define EPOLLWAKEUP (1 << 29)
+#define EPOLLWAKEUP (1U << 29)
/* Set the One Shot behaviour for the target file descriptor */
-#define EPOLLONESHOT (1 << 30)
+#define EPOLLONESHOT (1U << 30)
/* Set the Edge Triggered behaviour for the target file descriptor */
-#define EPOLLET (1 << 31)
+#define EPOLLET (1U << 31)
/*
* On x86-64 make the 64bit structure have the same alignment as the
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 9fbdc11..3092188 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -3782,6 +3782,9 @@
* @NL80211_BSS_PARENT_BSSID. (u64).
* @NL80211_BSS_PARENT_BSSID: the BSS according to which @NL80211_BSS_PARENT_TSF
* is set.
+ * @NL80211_BSS_CHAIN_SIGNAL: per-chain signal strength of last BSS update.
+ * Contains a nested array of signal strength attributes (u8, dBm),
+ * using the nesting index as the antenna number.
* @__NL80211_BSS_AFTER_LAST: internal
* @NL80211_BSS_MAX: highest BSS attribute
*/
@@ -3805,6 +3808,7 @@
NL80211_BSS_PAD,
NL80211_BSS_PARENT_TSF,
NL80211_BSS_PARENT_BSSID,
+ NL80211_BSS_CHAIN_SIGNAL,
/* keep last */
__NL80211_BSS_AFTER_LAST,
@@ -4835,6 +4839,27 @@
* RSSI threshold values to monitor rather than exactly one threshold.
* @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD: Driver SME supports FILS shared key
* authentication with %NL80211_CMD_CONNECT.
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK: Device wants to do 4-way
+ * handshake with PSK in station mode (PSK is passed as part of the connect
+ * and associate commands), doing it in the host might not be supported.
+ * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X: Device wants to do doing 4-way
+ * handshake with 802.1X in station mode (will pass EAP frames to the host
+ * and accept the set_pmk/del_pmk commands), doing it in the host might not
+ * be supported.
+ * @NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME: Driver is capable of overriding
+ * the max channel attribute in the FILS request params IE with the
+ * actual dwell time.
+ * @NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP: Driver accepts broadcast probe
+ * response
+ * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE: Driver supports sending
+ * the first probe request in each channel at rate of at least 5.5Mbps.
+ * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: Driver supports
+ * probe request tx deferral and suppression
+ * @NL80211_EXT_FEATURE_MFP_OPTIONAL: Driver supports the %NL80211_MFP_OPTIONAL
+ * value in %NL80211_ATTR_USE_MFP.
+ * @NL80211_EXT_FEATURE_LOW_SPAN_SCAN: Driver supports low span scan.
+ * @NL80211_EXT_FEATURE_LOW_POWER_SCAN: Driver supports low power scan.
+ * @NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN: Driver supports high accuracy scan.
*
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@@ -4855,6 +4880,16 @@
NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI,
NL80211_EXT_FEATURE_CQM_RSSI_LIST,
NL80211_EXT_FEATURE_FILS_SK_OFFLOAD,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X,
+ NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME,
+ NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE,
+ NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION,
+ NL80211_EXT_FEATURE_MFP_OPTIONAL,
+ NL80211_EXT_FEATURE_LOW_SPAN_SCAN,
+ NL80211_EXT_FEATURE_LOW_POWER_SCAN,
+ NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN,
/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
@@ -4915,6 +4950,10 @@
* of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN
* requests.
*
+ * NL80211_SCAN_FLAG_LOW_SPAN, NL80211_SCAN_FLAG_LOW_POWER, and
+ * NL80211_SCAN_FLAG_HIGH_ACCURACY flags are exclusive of each other, i.e., only
+ * one of them can be used in the request.
+ *
* @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority
* @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning
* @NL80211_SCAN_FLAG_AP: force a scan even if the interface is configured
@@ -4931,12 +4970,29 @@
* locally administered 1, multicast 0) is assumed.
* This flag must not be requested when the feature isn't supported, check
* the nl80211 feature flags for the device.
+ * SSID and/or RSSI.
+ * @NL80211_SCAN_FLAG_LOW_SPAN: Span corresponds to the total time taken to
+ * accomplish the scan. Thus, this flag intends the driver to perform the
+ * scan request with lesser span/duration. It is specific to the driver
+ * implementations on how this is accomplished. Scan accuracy may get
+ * impacted with this flag.
+ * @NL80211_SCAN_FLAG_LOW_POWER: This flag intends the scan attempts to consume
+ * optimal possible power. Drivers can resort to their specific means to
+ * optimize the power. Scan accuracy may get impacted with this flag.
+ * @NL80211_SCAN_FLAG_HIGH_ACCURACY: Accuracy here intends to the extent of scan
+ * results obtained. Thus HIGH_ACCURACY scan flag aims to get maximum
+ * possible scan results. This flag hints the driver to use the best
+ * possible scan configuration to improve the accuracy in scanning.
+ * Latency and power use may get impacted with this flag.
*/
enum nl80211_scan_flags {
NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0,
NL80211_SCAN_FLAG_FLUSH = 1<<1,
NL80211_SCAN_FLAG_AP = 1<<2,
NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3,
+ NL80211_SCAN_FLAG_LOW_SPAN = 1<<8,
+ NL80211_SCAN_FLAG_LOW_POWER = 1<<9,
+ NL80211_SCAN_FLAG_HIGH_ACCURACY = 1<<10,
};
/**
diff --git a/init/main.c b/init/main.c
index 674bc77..17e439f 100644
--- a/init/main.c
+++ b/init/main.c
@@ -80,6 +80,7 @@
#include <linux/integrity.h>
#include <linux/proc_ns.h>
#include <linux/io.h>
+#include <linux/kaiser.h>
#include <asm/io.h>
#include <asm/bugs.h>
@@ -473,6 +474,7 @@
pgtable_init();
vmalloc_init();
ioremap_huge_init();
+ kaiser_init();
}
asmlinkage __visible void __init start_kernel(void)
diff --git a/kernel/acct.c b/kernel/acct.c
index 74963d1..37f1dc6 100644
--- a/kernel/acct.c
+++ b/kernel/acct.c
@@ -99,7 +99,7 @@
{
struct kstatfs sbuf;
- if (time_is_before_jiffies(acct->needcheck))
+ if (time_is_after_jiffies(acct->needcheck))
goto out;
/* May block */
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c
index f3721e1..9a1e6ed 100644
--- a/kernel/bpf/arraymap.c
+++ b/kernel/bpf/arraymap.c
@@ -46,9 +46,10 @@
static struct bpf_map *array_map_alloc(union bpf_attr *attr)
{
bool percpu = attr->map_type == BPF_MAP_TYPE_PERCPU_ARRAY;
+ u32 elem_size, index_mask, max_entries;
+ bool unpriv = !capable(CAP_SYS_ADMIN);
struct bpf_array *array;
- u64 array_size;
- u32 elem_size;
+ u64 array_size, mask64;
/* check sanity of attributes */
if (attr->max_entries == 0 || attr->key_size != 4 ||
@@ -63,11 +64,32 @@
elem_size = round_up(attr->value_size, 8);
+ max_entries = attr->max_entries;
+
+ /* On 32 bit archs roundup_pow_of_two() with max_entries that has
+ * upper most bit set in u32 space is undefined behavior due to
+ * resulting 1U << 32, so do it manually here in u64 space.
+ */
+ mask64 = fls_long(max_entries - 1);
+ mask64 = 1ULL << mask64;
+ mask64 -= 1;
+
+ index_mask = mask64;
+ if (unpriv) {
+ /* round up array size to nearest power of 2,
+ * since cpu will speculate within index_mask limits
+ */
+ max_entries = index_mask + 1;
+ /* Check for overflows. */
+ if (max_entries < attr->max_entries)
+ return ERR_PTR(-E2BIG);
+ }
+
array_size = sizeof(*array);
if (percpu)
- array_size += (u64) attr->max_entries * sizeof(void *);
+ array_size += (u64) max_entries * sizeof(void *);
else
- array_size += (u64) attr->max_entries * elem_size;
+ array_size += (u64) max_entries * elem_size;
/* make sure there is no u32 overflow later in round_up() */
if (array_size >= U32_MAX - PAGE_SIZE)
@@ -77,6 +99,8 @@
array = bpf_map_area_alloc(array_size);
if (!array)
return ERR_PTR(-ENOMEM);
+ array->index_mask = index_mask;
+ array->map.unpriv_array = unpriv;
/* copy mandatory map attributes */
array->map.map_type = attr->map_type;
@@ -110,7 +134,7 @@
if (unlikely(index >= array->map.max_entries))
return NULL;
- return array->value + array->elem_size * index;
+ return array->value + array->elem_size * (index & array->index_mask);
}
/* Called from eBPF program */
@@ -122,7 +146,7 @@
if (unlikely(index >= array->map.max_entries))
return NULL;
- return this_cpu_ptr(array->pptrs[index]);
+ return this_cpu_ptr(array->pptrs[index & array->index_mask]);
}
int bpf_percpu_array_copy(struct bpf_map *map, void *key, void *value)
@@ -142,7 +166,7 @@
*/
size = round_up(map->value_size, 8);
rcu_read_lock();
- pptr = array->pptrs[index];
+ pptr = array->pptrs[index & array->index_mask];
for_each_possible_cpu(cpu) {
bpf_long_memcpy(value + off, per_cpu_ptr(pptr, cpu), size);
off += size;
@@ -190,10 +214,11 @@
return -EEXIST;
if (array->map.map_type == BPF_MAP_TYPE_PERCPU_ARRAY)
- memcpy(this_cpu_ptr(array->pptrs[index]),
+ memcpy(this_cpu_ptr(array->pptrs[index & array->index_mask]),
value, map->value_size);
else
- memcpy(array->value + array->elem_size * index,
+ memcpy(array->value +
+ array->elem_size * (index & array->index_mask),
value, map->value_size);
return 0;
}
@@ -227,7 +252,7 @@
*/
size = round_up(map->value_size, 8);
rcu_read_lock();
- pptr = array->pptrs[index];
+ pptr = array->pptrs[index & array->index_mask];
for_each_possible_cpu(cpu) {
bpf_long_memcpy(per_cpu_ptr(pptr, cpu), value + off, size);
off += size;
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index cd62aea..5b017fb 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -565,57 +565,6 @@
list_add(&tl->list_node, &bpf_prog_types);
}
-/* fixup insn->imm field of bpf_call instructions:
- * if (insn->imm == BPF_FUNC_map_lookup_elem)
- * insn->imm = bpf_map_lookup_elem - __bpf_call_base;
- * else if (insn->imm == BPF_FUNC_map_update_elem)
- * insn->imm = bpf_map_update_elem - __bpf_call_base;
- * else ...
- *
- * this function is called after eBPF program passed verification
- */
-static void fixup_bpf_calls(struct bpf_prog *prog)
-{
- const struct bpf_func_proto *fn;
- int i;
-
- for (i = 0; i < prog->len; i++) {
- struct bpf_insn *insn = &prog->insnsi[i];
-
- if (insn->code == (BPF_JMP | BPF_CALL)) {
- /* we reach here when program has bpf_call instructions
- * and it passed bpf_check(), means that
- * ops->get_func_proto must have been supplied, check it
- */
- BUG_ON(!prog->aux->ops->get_func_proto);
-
- if (insn->imm == BPF_FUNC_get_route_realm)
- prog->dst_needed = 1;
- if (insn->imm == BPF_FUNC_get_prandom_u32)
- bpf_user_rnd_init_once();
- if (insn->imm == BPF_FUNC_tail_call) {
- /* mark bpf_tail_call as different opcode
- * to avoid conditional branch in
- * interpeter for every normal call
- * and to prevent accidental JITing by
- * JIT compiler that doesn't support
- * bpf_tail_call yet
- */
- insn->imm = 0;
- insn->code |= BPF_X;
- continue;
- }
-
- fn = prog->aux->ops->get_func_proto(insn->imm);
- /* all functions that have prototype and verifier allowed
- * programs to call them, must be real in-kernel functions
- */
- BUG_ON(!fn->func);
- insn->imm = fn->func - __bpf_call_base;
- }
- }
-}
-
/* drop refcnt on maps used by eBPF program and free auxilary data */
static void free_used_maps(struct bpf_prog_aux *aux)
{
@@ -810,9 +759,6 @@
if (err < 0)
goto free_used_maps;
- /* fixup BPF_CALL->imm field */
- fixup_bpf_calls(prog);
-
/* eBPF program is ready to be JITed */
prog = bpf_prog_select_runtime(prog, &err);
if (err < 0)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index d7eeebf..19c44cf 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1187,7 +1187,7 @@
}
}
-static int check_call(struct bpf_verifier_env *env, int func_id)
+static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx)
{
struct bpf_verifier_state *state = &env->cur_state;
const struct bpf_func_proto *fn = NULL;
@@ -1238,6 +1238,13 @@
err = check_func_arg(env, BPF_REG_2, fn->arg2_type, &meta);
if (err)
return err;
+ if (func_id == BPF_FUNC_tail_call) {
+ if (meta.map_ptr == NULL) {
+ verbose("verifier bug\n");
+ return -EINVAL;
+ }
+ env->insn_aux_data[insn_idx].map_ptr = meta.map_ptr;
+ }
err = check_func_arg(env, BPF_REG_3, fn->arg3_type, &meta);
if (err)
return err;
@@ -3019,7 +3026,7 @@
return -EINVAL;
}
- err = check_call(env, insn->imm);
+ err = check_call(env, insn->imm, insn_idx);
if (err)
return err;
@@ -3362,6 +3369,81 @@
return 0;
}
+/* fixup insn->imm field of bpf_call instructions
+ *
+ * this function is called after eBPF program passed verification
+ */
+static int fixup_bpf_calls(struct bpf_verifier_env *env)
+{
+ struct bpf_prog *prog = env->prog;
+ struct bpf_insn *insn = prog->insnsi;
+ const struct bpf_func_proto *fn;
+ const int insn_cnt = prog->len;
+ struct bpf_insn insn_buf[16];
+ struct bpf_prog *new_prog;
+ struct bpf_map *map_ptr;
+ int i, cnt, delta = 0;
+
+
+ for (i = 0; i < insn_cnt; i++, insn++) {
+ if (insn->code != (BPF_JMP | BPF_CALL))
+ continue;
+
+ if (insn->imm == BPF_FUNC_get_route_realm)
+ prog->dst_needed = 1;
+ if (insn->imm == BPF_FUNC_get_prandom_u32)
+ bpf_user_rnd_init_once();
+ if (insn->imm == BPF_FUNC_tail_call) {
+ /* mark bpf_tail_call as different opcode to avoid
+ * conditional branch in the interpeter for every normal
+ * call and to prevent accidental JITing by JIT compiler
+ * that doesn't support bpf_tail_call yet
+ */
+ insn->imm = 0;
+ insn->code |= BPF_X;
+
+ /* instead of changing every JIT dealing with tail_call
+ * emit two extra insns:
+ * if (index >= max_entries) goto out;
+ * index &= array->index_mask;
+ * to avoid out-of-bounds cpu speculation
+ */
+ map_ptr = env->insn_aux_data[i + delta].map_ptr;
+ if (!map_ptr->unpriv_array)
+ continue;
+ insn_buf[0] = BPF_JMP_IMM(BPF_JGE, BPF_REG_3,
+ map_ptr->max_entries, 2);
+ insn_buf[1] = BPF_ALU32_IMM(BPF_AND, BPF_REG_3,
+ container_of(map_ptr,
+ struct bpf_array,
+ map)->index_mask);
+ insn_buf[2] = *insn;
+ cnt = 3;
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ if (!new_prog)
+ return -ENOMEM;
+
+ delta += cnt - 1;
+ env->prog = prog = new_prog;
+ insn = new_prog->insnsi + i + delta;
+ continue;
+ }
+
+ fn = prog->aux->ops->get_func_proto(insn->imm);
+ /* all functions that have prototype and verifier allowed
+ * programs to call them, must be real in-kernel functions
+ */
+ if (!fn->func) {
+ verbose("kernel subsystem misconfigured func %d\n",
+ insn->imm);
+ return -EFAULT;
+ }
+ insn->imm = fn->func - __bpf_call_base;
+ }
+
+ return 0;
+}
+
static void free_states(struct bpf_verifier_env *env)
{
struct bpf_verifier_state_list *sl, *sln;
@@ -3463,6 +3545,9 @@
/* program is valid, convert *(u32*)(ctx + off) accesses */
ret = convert_ctx_accesses(env);
+ if (ret == 0)
+ ret = fixup_bpf_calls(env);
+
if (log_level && log_len >= log_size - 1) {
BUG_ON(log_len >= log_size);
/* verifier log exceeded user supplied buffer */
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 26c624e..a83771f 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -4407,7 +4407,11 @@
*/
do {
css_task_iter_start(&from->self, &it);
- task = css_task_iter_next(&it);
+
+ do {
+ task = css_task_iter_next(&it);
+ } while (task && (task->flags & PF_EXITING));
+
if (task)
get_task_struct(task);
css_task_iter_end(&it);
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 823fe5a..c68d150 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -1376,9 +1376,9 @@
* before blk_mq_queue_reinit_notify() from notify_dead(),
* otherwise a RCU stall occurs.
*/
- [CPUHP_TIMERS_DEAD] = {
+ [CPUHP_TIMERS_PREPARE] = {
.name = "timers:dead",
- .startup.single = NULL,
+ .startup.single = timers_prepare_cpu,
.teardown.single = timers_dead_cpu,
},
/* Kicks the plugged cpu into life */
diff --git a/kernel/fork.c b/kernel/fork.c
index b83adf9..79fdfd8 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -58,6 +58,7 @@
#include <linux/tsacct_kern.h>
#include <linux/cn_proc.h>
#include <linux/freezer.h>
+#include <linux/kaiser.h>
#include <linux/delayacct.h>
#include <linux/taskstats_kern.h>
#include <linux/random.h>
@@ -213,6 +214,7 @@
static inline void free_thread_stack(struct task_struct *tsk)
{
+ kaiser_unmap_thread_stack(tsk->stack);
#ifdef CONFIG_VMAP_STACK
if (task_stack_vm_area(tsk)) {
unsigned long flags;
@@ -495,6 +497,10 @@
* functions again.
*/
tsk->stack = stack;
+
+ err= kaiser_map_thread_stack(tsk->stack);
+ if (err)
+ goto free_stack;
#ifdef CONFIG_VMAP_STACK
tsk->stack_vm_area = stack_vm_area;
#endif
diff --git a/kernel/groups.c b/kernel/groups.c
index 2fcadd6..94bde52 100644
--- a/kernel/groups.c
+++ b/kernel/groups.c
@@ -77,7 +77,7 @@
}
/* a simple Shell sort */
-static void groups_sort(struct group_info *group_info)
+void groups_sort(struct group_info *group_info)
{
int base, max, stride;
int gidsetsize = group_info->ngroups;
@@ -103,6 +103,7 @@
stride /= 3;
}
}
+EXPORT_SYMBOL(groups_sort);
/* a simple bsearch */
int groups_search(const struct group_info *group_info, kgid_t grp)
@@ -134,7 +135,6 @@
void set_groups(struct cred *new, struct group_info *group_info)
{
put_group_info(new->group_info);
- groups_sort(group_info);
get_group_info(group_info);
new->group_info = group_info;
}
@@ -218,6 +218,7 @@
return retval;
}
+ groups_sort(group_info);
retval = set_current_groups(group_info);
put_group_info(group_info);
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 59fcdbe..7b02ae6 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -1051,8 +1051,13 @@
*/
static struct rq *__migrate_task(struct rq *rq, struct task_struct *p, int dest_cpu)
{
- if (unlikely(!cpu_active(dest_cpu)))
- return rq;
+ if (p->flags & PF_KTHREAD) {
+ if (unlikely(!cpu_online(dest_cpu)))
+ return rq;
+ } else {
+ if (unlikely(!cpu_active(dest_cpu)))
+ return rq;
+ }
/* Affinity changed (again). */
if (!cpumask_test_cpu(dest_cpu, tsk_cpus_allowed(p)))
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 645b472..73f11c4 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -2178,9 +2178,8 @@
* the rt_loop_next will cause the iterator to perform another scan.
*
*/
-static int rto_next_cpu(struct rq *rq)
+static int rto_next_cpu(struct root_domain *rd)
{
- struct root_domain *rd = rq->rd;
int next;
int cpu;
@@ -2256,7 +2255,7 @@
* Otherwise it is finishing up and an ipi needs to be sent.
*/
if (rq->rd->rto_cpu < 0)
- cpu = rto_next_cpu(rq);
+ cpu = rto_next_cpu(rq->rd);
raw_spin_unlock(&rq->rd->rto_lock);
@@ -2269,6 +2268,8 @@
/* Called from hardirq context */
void rto_push_irq_work_func(struct irq_work *work)
{
+ struct root_domain *rd =
+ container_of(work, struct root_domain, rto_push_work);
struct rq *rq;
int cpu;
@@ -2284,18 +2285,18 @@
raw_spin_unlock(&rq->lock);
}
- raw_spin_lock(&rq->rd->rto_lock);
+ raw_spin_lock(&rd->rto_lock);
/* Pass the IPI to the next rt overloaded queue */
- cpu = rto_next_cpu(rq);
+ cpu = rto_next_cpu(rd);
- raw_spin_unlock(&rq->rd->rto_lock);
+ raw_spin_unlock(&rd->rto_lock);
if (cpu < 0)
return;
/* Try the next RT overloaded CPU */
- irq_work_queue_on(&rq->rd->rto_push_work, cpu);
+ irq_work_queue_on(&rd->rto_push_work, cpu);
}
#endif /* HAVE_RT_PUSH_IPI */
diff --git a/kernel/signal.c b/kernel/signal.c
index e48668c..7ebe236 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -72,7 +72,7 @@
handler = sig_handler(t, sig);
if (unlikely(t->signal->flags & SIGNAL_UNKILLABLE) &&
- handler == SIG_DFL && !force)
+ handler == SIG_DFL && !(force && sig_kernel_only(sig)))
return 1;
return sig_handler_ignored(handler, sig);
@@ -88,13 +88,15 @@
if (sigismember(&t->blocked, sig) || sigismember(&t->real_blocked, sig))
return 0;
- if (!sig_task_ignored(t, sig, force))
+ /*
+ * Tracers may want to know about even ignored signal unless it
+ * is SIGKILL which can't be reported anyway but can be ignored
+ * by SIGNAL_UNKILLABLE task.
+ */
+ if (t->ptrace && sig != SIGKILL)
return 0;
- /*
- * Tracers may want to know about even ignored signals.
- */
- return !t->ptrace;
+ return sig_task_ignored(t, sig, force);
}
/*
@@ -917,9 +919,9 @@
* then start taking the whole group down immediately.
*/
if (sig_fatal(p, sig) &&
- !(signal->flags & (SIGNAL_UNKILLABLE | SIGNAL_GROUP_EXIT)) &&
+ !(signal->flags & SIGNAL_GROUP_EXIT) &&
!sigismember(&t->real_blocked, sig) &&
- (sig == SIGKILL || !t->ptrace)) {
+ (sig == SIGKILL || !p->ptrace)) {
/*
* This signal will be fatal to the whole group.
*/
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index df3191e..4ce4285 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -684,6 +684,11 @@
tick_program_event(hrtimer_get_expires(&ts->sched_timer), 1);
}
+static inline bool local_timer_softirq_pending(void)
+{
+ return local_softirq_pending() & TIMER_SOFTIRQ;
+}
+
static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
ktime_t now, int cpu)
{
@@ -700,8 +705,18 @@
} while (read_seqretry(&jiffies_lock, seq));
ts->last_jiffies = basejiff;
- if (rcu_needs_cpu(basemono, &next_rcu) ||
- arch_needs_cpu() || irq_work_needs_cpu()) {
+ /*
+ * Keep the periodic tick, when RCU, architecture or irq_work
+ * requests it.
+ * Aside of that check whether the local timer softirq is
+ * pending. If so its a bad idea to call get_next_timer_interrupt()
+ * because there is an already expired timer, so it will request
+ * immeditate expiry, which rearms the hardware timer with a
+ * minimal delta which brings us back to this place
+ * immediately. Lather, rinse and repeat...
+ */
+ if (rcu_needs_cpu(basemono, &next_rcu) || arch_needs_cpu() ||
+ irq_work_needs_cpu() || local_timer_softirq_pending()) {
next_tick = basemono + TICK_NSEC;
} else {
/*
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 4c0b001..5b5d016 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -826,11 +826,10 @@
struct timer_base *base = per_cpu_ptr(&timer_bases[BASE_STD], cpu);
/*
- * If the timer is deferrable and nohz is active then we need to use
- * the deferrable base.
+ * If the timer is deferrable and NO_HZ_COMMON is set then we need
+ * to use the deferrable base.
*/
- if (IS_ENABLED(CONFIG_NO_HZ_COMMON) && base->nohz_active &&
- (tflags & TIMER_DEFERRABLE)) {
+ if (IS_ENABLED(CONFIG_NO_HZ_COMMON) && (tflags & TIMER_DEFERRABLE)) {
base = &timer_base_deferrable;
if (tflags & TIMER_PINNED)
base = per_cpu_ptr(&timer_bases[BASE_DEF], cpu);
@@ -843,11 +842,10 @@
struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]);
/*
- * If the timer is deferrable and nohz is active then we need to use
- * the deferrable base.
+ * If the timer is deferrable and NO_HZ_COMMON is set then we need
+ * to use the deferrable base.
*/
- if (IS_ENABLED(CONFIG_NO_HZ_COMMON) && base->nohz_active &&
- (tflags & TIMER_DEFERRABLE)) {
+ if (IS_ENABLED(CONFIG_NO_HZ_COMMON) && (tflags & TIMER_DEFERRABLE)) {
base = &timer_base_deferrable;
if (tflags & TIMER_PINNED)
base = this_cpu_ptr(&timer_bases[BASE_DEF]);
@@ -1002,8 +1000,6 @@
if (!ret && pending_only)
goto out_unlock;
- debug_activate(timer, expires);
-
new_base = get_target_base(base, timer->flags);
if (base != new_base) {
@@ -1027,6 +1023,8 @@
}
}
+ debug_activate(timer, expires);
+
timer->expires = expires;
/*
* If 'idx' was calculated above and the base time did not advance
@@ -1692,7 +1690,7 @@
base->must_forward_clk = false;
__run_timers(base);
- if (IS_ENABLED(CONFIG_NO_HZ_COMMON) && base->nohz_active)
+ if (IS_ENABLED(CONFIG_NO_HZ_COMMON))
__run_timers(this_cpu_ptr(&timer_bases[BASE_DEF]));
if ((atomic_cmpxchg(&deferrable_pending, 1, 0) &&
@@ -1872,6 +1870,21 @@
}
}
+int timers_prepare_cpu(unsigned int cpu)
+{
+ struct timer_base *base;
+ int b;
+
+ for (b = 0; b < NR_BASES; b++) {
+ base = per_cpu_ptr(&timer_bases[b], cpu);
+ base->clk = jiffies;
+ base->next_expiry = base->clk + NEXT_TIMER_MAX_DELTA;
+ base->is_idle = false;
+ base->must_forward_clk = true;
+ }
+ return 0;
+}
+
static void __migrate_timers(unsigned int cpu, bool remove_pinned)
{
struct timer_base *old_base;
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index f5c016e..3e1d11f 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -280,6 +280,8 @@
/* Missed count stored at end */
#define RB_MISSED_STORED (1 << 30)
+#define RB_MISSED_FLAGS (RB_MISSED_EVENTS|RB_MISSED_STORED)
+
struct buffer_data_page {
u64 time_stamp; /* page time stamp */
local_t commit; /* write committed index */
@@ -331,7 +333,9 @@
*/
size_t ring_buffer_page_len(void *page)
{
- return local_read(&((struct buffer_data_page *)page)->commit)
+ struct buffer_data_page *bpage = page;
+
+ return (local_read(&bpage->commit) & ~RB_MISSED_FLAGS)
+ BUF_PAGE_HDR_SIZE;
}
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 9e862ae..046abb0 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -6337,7 +6337,7 @@
.spd_release = buffer_spd_release,
};
struct buffer_ref *ref;
- int entries, size, i;
+ int entries, i;
ssize_t ret = 0;
#ifdef CONFIG_TRACER_MAX_TRACE
@@ -6388,14 +6388,6 @@
break;
}
- /*
- * zero out any left over data, this is going to
- * user land.
- */
- size = ring_buffer_page_len(ref->page);
- if (size < PAGE_SIZE)
- memset(ref->page + size, 0, PAGE_SIZE - size);
-
page = virt_to_page(ref->page);
spd.pages[i] = page;
@@ -7119,6 +7111,7 @@
buf->data = alloc_percpu(struct trace_array_cpu);
if (!buf->data) {
ring_buffer_free(buf->buffer);
+ buf->buffer = NULL;
return -ENOMEM;
}
@@ -7142,7 +7135,9 @@
allocate_snapshot ? size : 1);
if (WARN_ON(ret)) {
ring_buffer_free(tr->trace_buffer.buffer);
+ tr->trace_buffer.buffer = NULL;
free_percpu(tr->trace_buffer.data);
+ tr->trace_buffer.data = NULL;
return -ENOMEM;
}
tr->allocated_snapshot = allocate_snapshot;
diff --git a/kernel/uid16.c b/kernel/uid16.c
index cc40793..dcffcce 100644
--- a/kernel/uid16.c
+++ b/kernel/uid16.c
@@ -190,6 +190,7 @@
return retval;
}
+ groups_sort(group_info);
retval = set_current_groups(group_info);
put_group_info(group_info);
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 6b9af20..9aea3480 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -1641,7 +1641,7 @@
mod_timer(&pool->idle_timer, jiffies + IDLE_WORKER_TIMEOUT);
/*
- * Sanity check nr_running. Because wq_unbind_fn() releases
+ * Sanity check nr_running. Because unbind_workers() releases
* pool->lock between setting %WORKER_UNBOUND and zapping
* nr_running, the warning may trigger spuriously. Check iff
* unbind is not in progress.
@@ -4478,9 +4478,8 @@
* cpu comes back online.
*/
-static void wq_unbind_fn(struct work_struct *work)
+static void unbind_workers(int cpu)
{
- int cpu = smp_processor_id();
struct worker_pool *pool;
struct worker *worker;
@@ -4677,12 +4676,13 @@
int workqueue_offline_cpu(unsigned int cpu)
{
- struct work_struct unbind_work;
struct workqueue_struct *wq;
/* unbinding per-cpu workers should happen on the local CPU */
- INIT_WORK_ONSTACK(&unbind_work, wq_unbind_fn);
- queue_work_on(cpu, system_highpri_wq, &unbind_work);
+ if (WARN_ON(cpu != smp_processor_id()))
+ return -1;
+
+ unbind_workers(cpu);
/* update NUMA affinity of unbound workqueues */
mutex_lock(&wq_pool_mutex);
@@ -4690,9 +4690,6 @@
wq_update_unbound_numa(wq, cpu, false);
mutex_unlock(&wq_pool_mutex);
- /* wait for per-cpu unbinding to finish */
- flush_work(&unbind_work);
- destroy_work_on_stack(&unbind_work);
return 0;
}
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index fce6c48..37d63b2 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -1619,9 +1619,13 @@
* @page: the page
*
* This function protects unlocked LRU pages from being moved to
- * another cgroup and stabilizes their page->mem_cgroup binding.
+ * another cgroup.
+ *
+ * It ensures lifetime of the returned memcg. Caller is responsible
+ * for the lifetime of the page; __unlock_page_memcg() is available
+ * when @page might get freed inside the locked section.
*/
-void lock_page_memcg(struct page *page)
+struct mem_cgroup *lock_page_memcg(struct page *page)
{
struct mem_cgroup *memcg;
unsigned long flags;
@@ -1630,18 +1634,24 @@
* The RCU lock is held throughout the transaction. The fast
* path can get away without acquiring the memcg->move_lock
* because page moving starts with an RCU grace period.
- */
+ *
+ * The RCU lock also protects the memcg from being freed when
+ * the page state that is going to change is the only thing
+ * preventing the page itself from being freed. E.g. writeback
+ * doesn't hold a page reference and relies on PG_writeback to
+ * keep off truncation, migration and so forth.
+ */
rcu_read_lock();
if (mem_cgroup_disabled())
- return;
+ return NULL;
again:
memcg = page->mem_cgroup;
if (unlikely(!memcg))
- return;
+ return NULL;
if (atomic_read(&memcg->moving_account) <= 0)
- return;
+ return memcg;
spin_lock_irqsave(&memcg->move_lock, flags);
if (memcg != page->mem_cgroup) {
@@ -1657,18 +1667,18 @@
memcg->move_lock_task = current;
memcg->move_lock_flags = flags;
- return;
+ return memcg;
}
EXPORT_SYMBOL(lock_page_memcg);
/**
- * unlock_page_memcg - unlock a page->mem_cgroup binding
- * @page: the page
+ * __unlock_page_memcg - unlock and unpin a memcg
+ * @memcg: the memcg
+ *
+ * Unlock and unpin a memcg returned by lock_page_memcg().
*/
-void unlock_page_memcg(struct page *page)
+void __unlock_page_memcg(struct mem_cgroup *memcg)
{
- struct mem_cgroup *memcg = page->mem_cgroup;
-
if (memcg && memcg->move_lock_task == current) {
unsigned long flags = memcg->move_lock_flags;
@@ -1680,6 +1690,15 @@
rcu_read_unlock();
}
+
+/**
+ * unlock_page_memcg - unlock a page->mem_cgroup binding
+ * @page: the page
+ */
+void unlock_page_memcg(struct page *page)
+{
+ __unlock_page_memcg(page->mem_cgroup);
+}
EXPORT_SYMBOL(unlock_page_memcg);
/*
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index 439cc63..dd7817cd3 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -2713,9 +2713,10 @@
int test_clear_page_writeback(struct page *page)
{
struct address_space *mapping = page_mapping(page);
+ struct mem_cgroup *memcg;
int ret;
- lock_page_memcg(page);
+ memcg = lock_page_memcg(page);
if (mapping && mapping_use_writeback_tags(mapping)) {
struct inode *inode = mapping->host;
struct backing_dev_info *bdi = inode_to_bdi(inode);
@@ -2743,13 +2744,20 @@
} else {
ret = TestClearPageWriteback(page);
}
+ /*
+ * NOTE: Page might be free now! Writeback doesn't hold a page
+ * reference on its own, it relies on truncation to wait for
+ * the clearing of PG_writeback. The below can only access
+ * page state that is static across allocation cycles.
+ */
if (ret) {
- mem_cgroup_dec_page_stat(page, MEM_CGROUP_STAT_WRITEBACK);
+ __mem_cgroup_update_page_stat(page, memcg,
+ MEM_CGROUP_STAT_WRITEBACK, -1);
dec_node_page_state(page, NR_WRITEBACK);
dec_zone_page_state(page, NR_ZONE_WRITE_PENDING);
inc_node_page_state(page, NR_WRITTEN);
}
- unlock_page_memcg(page);
+ __unlock_page_memcg(memcg);
return ret;
}
diff --git a/mm/vmstat.c b/mm/vmstat.c
index 2ab7973..26f0e49 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -932,6 +932,7 @@
"nr_slab_unreclaimable",
"nr_page_table_pages",
"nr_kernel_stack",
+ "nr_overhead",
"nr_bounce",
#if IS_ENABLED(CONFIG_ZSMALLOC)
"nr_zspages",
diff --git a/mm/zswap.c b/mm/zswap.c
index dbef278..ded051e 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -752,18 +752,22 @@
pool = zswap_pool_find_get(type, compressor);
if (pool) {
zswap_pool_debug("using existing", pool);
+ WARN_ON(pool == zswap_pool_current());
list_del_rcu(&pool->list);
- } else {
- spin_unlock(&zswap_pools_lock);
- pool = zswap_pool_create(type, compressor);
- spin_lock(&zswap_pools_lock);
}
+ spin_unlock(&zswap_pools_lock);
+
+ if (!pool)
+ pool = zswap_pool_create(type, compressor);
+
if (pool)
ret = param_set_charp(s, kp);
else
ret = -EINVAL;
+ spin_lock(&zswap_pools_lock);
+
if (!ret) {
put_pool = zswap_pool_current();
list_add_rcu(&pool->list, &zswap_pools);
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index 4a47074..c8ea3cf 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -111,12 +111,7 @@
vlan_gvrp_uninit_applicant(real_dev);
}
- /* Take it out of our own structures, but be sure to interlock with
- * HW accelerating devices or SW vlan input packet processing if
- * VLAN is not 0 (leave it there for 802.1p).
- */
- if (vlan_id)
- vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id);
+ vlan_vid_del(real_dev, vlan->vlan_proto, vlan_id);
/* Get rid of the vlan's reference to real_dev */
dev_put(real_dev);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ffd09c1..2bbca23 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -3353,9 +3353,10 @@
break;
case L2CAP_CONF_EFS:
- remote_efs = 1;
- if (olen == sizeof(efs))
+ if (olen == sizeof(efs)) {
+ remote_efs = 1;
memcpy(&efs, (void *) val, olen);
+ }
break;
case L2CAP_CONF_EWS:
@@ -3574,16 +3575,17 @@
break;
case L2CAP_CONF_EFS:
- if (olen == sizeof(efs))
+ if (olen == sizeof(efs)) {
memcpy(&efs, (void *)val, olen);
- if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
- efs.stype != L2CAP_SERV_NOTRAFIC &&
- efs.stype != chan->local_stype)
- return -ECONNREFUSED;
+ if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
+ efs.stype != L2CAP_SERV_NOTRAFIC &&
+ efs.stype != chan->local_stype)
+ return -ECONNREFUSED;
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs),
- (unsigned long) &efs, endptr - ptr);
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs),
+ (unsigned long) &efs, endptr - ptr);
+ }
break;
case L2CAP_CONF_FCS:
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 5d4006e..4f83122 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -1092,19 +1092,20 @@
struct net_bridge *br = netdev_priv(dev);
int err;
+ err = register_netdevice(dev);
+ if (err)
+ return err;
+
if (tb[IFLA_ADDRESS]) {
spin_lock_bh(&br->lock);
br_stp_change_bridge_id(br, nla_data(tb[IFLA_ADDRESS]));
spin_unlock_bh(&br->lock);
}
- err = register_netdevice(dev);
- if (err)
- return err;
-
err = br_changelink(dev, tb, data);
if (err)
- unregister_netdevice(dev);
+ br_dev_delete(dev, NULL);
+
return err;
}
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index e9989b8..7913771 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -742,15 +742,6 @@
return dev->ethtool_ops->set_link_ksettings(dev, &link_ksettings);
}
-static void
-warn_incomplete_ethtool_legacy_settings_conversion(const char *details)
-{
- char name[sizeof(current->comm)];
-
- pr_info_once("warning: `%s' uses legacy ethtool link settings API, %s\n",
- get_task_comm(name, current), details);
-}
-
/* Query device for its ethtool_cmd settings.
*
* Backward compatibility note: for compatibility with legacy ethtool,
@@ -777,10 +768,8 @@
&link_ksettings);
if (err < 0)
return err;
- if (!convert_link_ksettings_to_legacy_settings(&cmd,
- &link_ksettings))
- warn_incomplete_ethtool_legacy_settings_conversion(
- "link modes are only partially reported");
+ convert_link_ksettings_to_legacy_settings(&cmd,
+ &link_ksettings);
/* send a sensible cmd tag back to user */
cmd.cmd = ETHTOOL_GSET;
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 7001da9..b7efe2f 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -263,7 +263,7 @@
spin_lock_irqsave(&net->nsid_lock, flags);
peer = idr_find(&net->netns_ids, id);
if (peer)
- get_net(peer);
+ peer = maybe_get_net(peer);
spin_unlock_irqrestore(&net->nsid_lock, flags);
rcu_read_unlock();
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index d7ecf40..26ef78a 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3829,7 +3829,7 @@
struct sock *sk = skb->sk;
if (!skb_may_tx_timestamp(sk, false))
- return;
+ goto err;
/* Take a reference to prevent skb_orphan() from freeing the socket,
* but only if the socket refcount is not zero.
@@ -3838,7 +3838,11 @@
*skb_hwtstamps(skb) = *hwtstamps;
__skb_complete_tx_timestamp(skb, sk, SCM_TSTAMP_SND);
sock_put(sk);
+ return;
}
+
+err:
+ kfree_skb(skb);
}
EXPORT_SYMBOL_GPL(skb_complete_tx_timestamp);
diff --git a/net/core/sock_diag.c b/net/core/sock_diag.c
index acd2a6c..fb467db 100644
--- a/net/core/sock_diag.c
+++ b/net/core/sock_diag.c
@@ -295,7 +295,7 @@
case SKNLGRP_INET6_UDP_DESTROY:
if (!sock_diag_handlers[AF_INET6])
request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
- NETLINK_SOCK_DIAG, AF_INET);
+ NETLINK_SOCK_DIAG, AF_INET6);
break;
}
return 0;
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index db54461..4457e5d 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -1380,7 +1380,7 @@
static bool inetdev_valid_mtu(unsigned int mtu)
{
- return mtu >= 68;
+ return mtu >= IPV4_MIN_MTU;
}
static void inetdev_send_gratuitous_arp(struct net_device *dev,
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 2ec005c..08b7260 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -1254,14 +1254,19 @@
static void ip_fib_net_exit(struct net *net)
{
- unsigned int i;
+ int i;
rtnl_lock();
#ifdef CONFIG_IP_MULTIPLE_TABLES
RCU_INIT_POINTER(net->ipv4.fib_main, NULL);
RCU_INIT_POINTER(net->ipv4.fib_default, NULL);
#endif
- for (i = 0; i < FIB_TABLE_HASHSZ; i++) {
+ /* Destroy the tables in reverse order to guarantee that the
+ * local table, ID 255, is destroyed before the main table, ID
+ * 254. This is necessary as the local table may contain
+ * references to data contained in the main table.
+ */
+ for (i = FIB_TABLE_HASHSZ - 1; i >= 0; i--) {
struct hlist_head *head = &net->ipv4.fib_table_hash[i];
struct hlist_node *tmp;
struct fib_table *tb;
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 08575e3..7bff0c6 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -89,6 +89,7 @@
#include <linux/rtnetlink.h>
#include <linux/times.h>
#include <linux/pkt_sched.h>
+#include <linux/byteorder/generic.h>
#include <net/net_namespace.h>
#include <net/arp.h>
@@ -321,6 +322,23 @@
return scount;
}
+/* source address selection per RFC 3376 section 4.2.13 */
+static __be32 igmpv3_get_srcaddr(struct net_device *dev,
+ const struct flowi4 *fl4)
+{
+ struct in_device *in_dev = __in_dev_get_rcu(dev);
+
+ if (!in_dev)
+ return htonl(INADDR_ANY);
+
+ for_ifa(in_dev) {
+ if (inet_ifa_match(fl4->saddr, ifa))
+ return fl4->saddr;
+ } endfor_ifa(in_dev);
+
+ return htonl(INADDR_ANY);
+}
+
static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu)
{
struct sk_buff *skb;
@@ -368,7 +386,7 @@
pip->frag_off = htons(IP_DF);
pip->ttl = 1;
pip->daddr = fl4.daddr;
- pip->saddr = fl4.saddr;
+ pip->saddr = igmpv3_get_srcaddr(dev, &fl4);
pip->protocol = IPPROTO_IGMP;
pip->tot_len = 0; /* filled in later */
ip_select_ident(net, skb, NULL);
@@ -404,16 +422,17 @@
}
static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc,
- int type, struct igmpv3_grec **ppgr)
+ int type, struct igmpv3_grec **ppgr, unsigned int mtu)
{
struct net_device *dev = pmc->interface->dev;
struct igmpv3_report *pih;
struct igmpv3_grec *pgr;
- if (!skb)
- skb = igmpv3_newpack(dev, dev->mtu);
- if (!skb)
- return NULL;
+ if (!skb) {
+ skb = igmpv3_newpack(dev, mtu);
+ if (!skb)
+ return NULL;
+ }
pgr = (struct igmpv3_grec *)skb_put(skb, sizeof(struct igmpv3_grec));
pgr->grec_type = type;
pgr->grec_auxwords = 0;
@@ -436,12 +455,17 @@
struct igmpv3_grec *pgr = NULL;
struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list;
int scount, stotal, first, isquery, truncate;
+ unsigned int mtu;
if (pmc->multiaddr == IGMP_ALL_HOSTS)
return skb;
if (ipv4_is_local_multicast(pmc->multiaddr) && !net->ipv4.sysctl_igmp_llm_reports)
return skb;
+ mtu = READ_ONCE(dev->mtu);
+ if (mtu < IPV4_MIN_MTU)
+ return skb;
+
isquery = type == IGMPV3_MODE_IS_INCLUDE ||
type == IGMPV3_MODE_IS_EXCLUDE;
truncate = type == IGMPV3_MODE_IS_EXCLUDE ||
@@ -462,7 +486,7 @@
AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) {
if (skb)
igmpv3_sendpack(skb);
- skb = igmpv3_newpack(dev, dev->mtu);
+ skb = igmpv3_newpack(dev, mtu);
}
}
first = 1;
@@ -498,12 +522,12 @@
pgr->grec_nsrcs = htons(scount);
if (skb)
igmpv3_sendpack(skb);
- skb = igmpv3_newpack(dev, dev->mtu);
+ skb = igmpv3_newpack(dev, mtu);
first = 1;
scount = 0;
}
if (first) {
- skb = add_grhead(skb, pmc, type, &pgr);
+ skb = add_grhead(skb, pmc, type, &pgr, mtu);
first = 0;
}
if (!skb)
@@ -538,7 +562,7 @@
igmpv3_sendpack(skb);
skb = NULL; /* add_grhead will get a new one */
}
- skb = add_grhead(skb, pmc, type, &pgr);
+ skb = add_grhead(skb, pmc, type, &pgr, mtu);
}
}
if (pgr)
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index bd7f183..96536a0 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -346,8 +346,8 @@
dev->needed_headroom = t_hlen + hlen;
mtu -= (dev->hard_header_len + t_hlen);
- if (mtu < 68)
- mtu = 68;
+ if (mtu < IPV4_MIN_MTU)
+ mtu = IPV4_MIN_MTU;
return mtu;
}
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index ab0bbcb..a860df2 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -502,11 +502,16 @@
int err;
struct ip_options_data opt_copy;
struct raw_frag_vec rfv;
+ int hdrincl;
err = -EMSGSIZE;
if (len > 0xFFFF)
goto out;
+ /* hdrincl should be READ_ONCE(inet->hdrincl)
+ * but READ_ONCE() doesn't work with bit fields
+ */
+ hdrincl = inet->hdrincl;
/*
* Check the flags.
*/
@@ -582,7 +587,7 @@
/* Linux does not mangle headers on raw sockets,
* so that IP options + IP_HDRINCL is non-sense.
*/
- if (inet->hdrincl)
+ if (hdrincl)
goto done;
if (ipc.opt->opt.srr) {
if (!daddr)
@@ -604,12 +609,12 @@
flowi4_init_output(&fl4, ipc.oif, sk->sk_mark, tos,
RT_SCOPE_UNIVERSE,
- inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol,
+ hdrincl ? IPPROTO_RAW : sk->sk_protocol,
inet_sk_flowi_flags(sk) |
- (inet->hdrincl ? FLOWI_FLAG_KNOWN_NH : 0),
+ (hdrincl ? FLOWI_FLAG_KNOWN_NH : 0),
daddr, saddr, 0, 0, sk->sk_uid);
- if (!inet->hdrincl) {
+ if (!hdrincl) {
rfv.msg = msg;
rfv.hlen = 0;
@@ -634,7 +639,7 @@
goto do_confirm;
back_from_confirm:
- if (inet->hdrincl)
+ if (hdrincl)
err = raw_send_hdrinc(sk, &fl4, msg, len,
&rt, msg->msg_flags, &ipc.sockc);
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 0a57417..bf071f3 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2306,6 +2306,7 @@
tp->snd_cwnd_cnt = 0;
tp->window_clamp = 0;
tcp_set_ca_state(sk, TCP_CA_Open);
+ tp->is_sack_reneg = 0;
tcp_clear_retrans(tp);
inet_csk_delack_init(sk);
/* Initialize rcv_mss to TCP_MIN_MSS to avoid division by 0
diff --git a/net/ipv4/tcp_bbr.c b/net/ipv4/tcp_bbr.c
index cb8db34..e86a34f 100644
--- a/net/ipv4/tcp_bbr.c
+++ b/net/ipv4/tcp_bbr.c
@@ -81,7 +81,8 @@
u32 lt_last_lost; /* LT intvl start: tp->lost */
u32 pacing_gain:10, /* current gain for setting pacing rate */
cwnd_gain:10, /* current gain for setting cwnd */
- full_bw_cnt:3, /* number of rounds without large bw gains */
+ full_bw_reached:1, /* reached full bw in Startup? */
+ full_bw_cnt:2, /* number of rounds without large bw gains */
cycle_idx:3, /* current index in pacing_gain cycle array */
has_seen_rtt:1, /* have we seen an RTT sample yet? */
unused_b:5;
@@ -151,7 +152,7 @@
{
const struct bbr *bbr = inet_csk_ca(sk);
- return bbr->full_bw_cnt >= bbr_full_bw_cnt;
+ return bbr->full_bw_reached;
}
/* Return the windowed max recent bandwidth sample, in pkts/uS << BW_SCALE. */
@@ -688,6 +689,7 @@
return;
}
++bbr->full_bw_cnt;
+ bbr->full_bw_reached = bbr->full_bw_cnt >= bbr_full_bw_cnt;
}
/* If pipe is probably full, drain the queue and then enter steady-state. */
@@ -821,6 +823,7 @@
bbr->restore_cwnd = 0;
bbr->round_start = 0;
bbr->idle_restart = 0;
+ bbr->full_bw_reached = 0;
bbr->full_bw = 0;
bbr->full_bw_cnt = 0;
bbr->cycle_mstamp.v64 = 0;
@@ -840,6 +843,11 @@
*/
static u32 bbr_undo_cwnd(struct sock *sk)
{
+ struct bbr *bbr = inet_csk_ca(sk);
+
+ bbr->full_bw = 0; /* spurious slow-down; reset full pipe detection */
+ bbr->full_bw_cnt = 0;
+ bbr_reset_lt_bw_sampling(sk);
return tcp_sk(sk)->snd_cwnd;
}
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index b6a7f91..8ce03d8 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -1967,6 +1967,8 @@
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPSACKRENEGING);
tp->sacked_out = 0;
tp->fackets_out = 0;
+ /* Mark SACK reneging until we recover from this loss event. */
+ tp->is_sack_reneg = 1;
}
tcp_clear_all_retrans_hints(tp);
@@ -2464,6 +2466,7 @@
return true;
}
tcp_set_ca_state(sk, TCP_CA_Open);
+ tp->is_sack_reneg = 0;
return false;
}
@@ -2495,8 +2498,10 @@
NET_INC_STATS(sock_net(sk),
LINUX_MIB_TCPSPURIOUSRTOS);
inet_csk(sk)->icsk_retransmits = 0;
- if (frto_undo || tcp_is_sack(tp))
+ if (frto_undo || tcp_is_sack(tp)) {
tcp_set_ca_state(sk, TCP_CA_Open);
+ tp->is_sack_reneg = 0;
+ }
return true;
}
return false;
@@ -3590,6 +3595,7 @@
struct tcp_sacktag_state sack_state;
struct rate_sample rs = { .prior_delivered = 0 };
u32 prior_snd_una = tp->snd_una;
+ bool is_sack_reneg = tp->is_sack_reneg;
u32 ack_seq = TCP_SKB_CB(skb)->seq;
u32 ack = TCP_SKB_CB(skb)->ack_seq;
bool is_dupack = false;
@@ -3712,7 +3718,7 @@
tcp_schedule_loss_probe(sk);
delivered = tp->delivered - delivered; /* freshly ACKed or SACKed */
lost = tp->lost - lost; /* freshly marked lost */
- tcp_rate_gen(sk, delivered, lost, &now, &rs);
+ tcp_rate_gen(sk, delivered, lost, is_sack_reneg, &now, &rs);
tcp_cong_control(sk, ack, delivered, flag, &rs);
tcp_xmit_recovery(sk, rexmit);
return 1;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 49d32fbc..05ac17a 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -831,7 +831,7 @@
tcp_time_stamp,
req->ts_recent,
0,
- tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&ip_hdr(skb)->daddr,
+ tcp_md5_do_lookup(sk, (union tcp_md5_addr *)&ip_hdr(skb)->saddr,
AF_INET),
inet_rsk(req)->no_srccheck ? IP_REPLY_ARG_NOSRCCHECK : 0,
ip_hdr(skb)->tos);
diff --git a/net/ipv4/tcp_rate.c b/net/ipv4/tcp_rate.c
index 9be1581..18309f5 100644
--- a/net/ipv4/tcp_rate.c
+++ b/net/ipv4/tcp_rate.c
@@ -106,7 +106,7 @@
/* Update the connection delivery information and generate a rate sample. */
void tcp_rate_gen(struct sock *sk, u32 delivered, u32 lost,
- struct skb_mstamp *now, struct rate_sample *rs)
+ bool is_sack_reneg, struct skb_mstamp *now, struct rate_sample *rs)
{
struct tcp_sock *tp = tcp_sk(sk);
u32 snd_us, ack_us;
@@ -124,8 +124,12 @@
rs->acked_sacked = delivered; /* freshly ACKed or SACKed */
rs->losses = lost; /* freshly marked lost */
- /* Return an invalid sample if no timing information is available. */
- if (!rs->prior_mstamp.v64) {
+ /* Return an invalid sample if no timing information is available or
+ * in recovery from loss with SACK reneging. Rate samples taken during
+ * a SACK reneging event may overestimate bw by including packets that
+ * were SACKed before the reneg.
+ */
+ if (!rs->prior_mstamp.v64 || is_sack_reneg) {
rs->delivered = -1;
rs->interval_us = -1;
return;
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index f51f94b..55a8f68 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -225,7 +225,6 @@
np->mcast_hops = IPV6_DEFAULT_MCASTHOPS;
np->mc_loop = 1;
np->pmtudisc = IPV6_PMTUDISC_WANT;
- np->autoflowlabel = ip6_default_np_autolabel(sock_net(sk));
sk->sk_ipv6only = net->ipv6.sysctl.bindv6only;
/* Init the ipv4 part of the socket since we can have sockets
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 4e6c439..dd93836 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -165,6 +165,14 @@
!(IP6CB(skb)->flags & IP6SKB_REROUTED));
}
+static bool ip6_autoflowlabel(struct net *net, const struct ipv6_pinfo *np)
+{
+ if (!np->autoflowlabel_set)
+ return ip6_default_np_autolabel(net);
+ else
+ return np->autoflowlabel;
+}
+
/*
* xmit an sk_buff (used by TCP, SCTP and DCCP)
* Note : socket lock is not held for SYNACK packets, but might be modified
@@ -228,7 +236,7 @@
hlimit = ip6_dst_hoplimit(dst);
ip6_flow_hdr(hdr, tclass, ip6_make_flowlabel(net, skb, fl6->flowlabel,
- np->autoflowlabel, fl6));
+ ip6_autoflowlabel(net, np), fl6));
hdr->payload_len = htons(seg_len);
hdr->nexthdr = proto;
@@ -1699,7 +1707,7 @@
ip6_flow_hdr(hdr, v6_cork->tclass,
ip6_make_flowlabel(net, skb, fl6->flowlabel,
- np->autoflowlabel, fl6));
+ ip6_autoflowlabel(net, np), fl6));
hdr->hop_limit = v6_cork->hop_limit;
hdr->nexthdr = proto;
hdr->saddr = fl6->saddr;
@@ -1800,9 +1808,10 @@
cork.base.opt = NULL;
v6_cork.opt = NULL;
err = ip6_setup_cork(sk, &cork, &v6_cork, ipc6, rt, fl6);
- if (err)
+ if (err) {
+ ip6_cork_release(&cork, &v6_cork);
return ERR_PTR(err);
-
+ }
if (ipc6->dontfrag < 0)
ipc6->dontfrag = inet6_sk(sk)->dontfrag;
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 64aefc2..4c52236 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -911,7 +911,7 @@
if (t->parms.collect_md) {
tun_dst = ipv6_tun_rx_dst(skb, 0, 0, 0);
if (!tun_dst)
- return 0;
+ goto drop;
}
ret = __ip6_tnl_rcv(t, skb, tpi, tun_dst, dscp_ecn_decapsulate,
log_ecn_error);
@@ -1080,10 +1080,11 @@
memcpy(&fl6->daddr, addr6, sizeof(fl6->daddr));
neigh_release(neigh);
}
- } else if (!(t->parms.flags &
- (IP6_TNL_F_USE_ORIG_TCLASS | IP6_TNL_F_USE_ORIG_FWMARK))) {
- /* enable the cache only only if the routing decision does
- * not depend on the current inner header value
+ } else if (t->parms.proto != 0 && !(t->parms.flags &
+ (IP6_TNL_F_USE_ORIG_TCLASS |
+ IP6_TNL_F_USE_ORIG_FWMARK))) {
+ /* enable the cache only if neither the outer protocol nor the
+ * routing decision depends on the current inner header value
*/
use_cache = true;
}
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 38bee17..6e3871c 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -874,6 +874,7 @@
break;
case IPV6_AUTOFLOWLABEL:
np->autoflowlabel = valbool;
+ np->autoflowlabel_set = 1;
retv = 0;
break;
}
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 1bdc703..ca8fac6 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1682,16 +1682,16 @@
}
static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc,
- int type, struct mld2_grec **ppgr)
+ int type, struct mld2_grec **ppgr, unsigned int mtu)
{
- struct net_device *dev = pmc->idev->dev;
struct mld2_report *pmr;
struct mld2_grec *pgr;
- if (!skb)
- skb = mld_newpack(pmc->idev, dev->mtu);
- if (!skb)
- return NULL;
+ if (!skb) {
+ skb = mld_newpack(pmc->idev, mtu);
+ if (!skb)
+ return NULL;
+ }
pgr = (struct mld2_grec *)skb_put(skb, sizeof(struct mld2_grec));
pgr->grec_type = type;
pgr->grec_auxwords = 0;
@@ -1714,10 +1714,15 @@
struct mld2_grec *pgr = NULL;
struct ip6_sf_list *psf, *psf_next, *psf_prev, **psf_list;
int scount, stotal, first, isquery, truncate;
+ unsigned int mtu;
if (pmc->mca_flags & MAF_NOREPORT)
return skb;
+ mtu = READ_ONCE(dev->mtu);
+ if (mtu < IPV6_MIN_MTU)
+ return skb;
+
isquery = type == MLD2_MODE_IS_INCLUDE ||
type == MLD2_MODE_IS_EXCLUDE;
truncate = type == MLD2_MODE_IS_EXCLUDE ||
@@ -1738,7 +1743,7 @@
AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) {
if (skb)
mld_sendpack(skb);
- skb = mld_newpack(idev, dev->mtu);
+ skb = mld_newpack(idev, mtu);
}
}
first = 1;
@@ -1774,12 +1779,12 @@
pgr->grec_nsrcs = htons(scount);
if (skb)
mld_sendpack(skb);
- skb = mld_newpack(idev, dev->mtu);
+ skb = mld_newpack(idev, mtu);
first = 1;
scount = 0;
}
if (first) {
- skb = add_grhead(skb, pmc, type, &pgr);
+ skb = add_grhead(skb, pmc, type, &pgr, mtu);
first = 0;
}
if (!skb)
@@ -1814,7 +1819,7 @@
mld_sendpack(skb);
skb = NULL; /* add_grhead will get a new one */
}
- skb = add_grhead(skb, pmc, type, &pgr);
+ skb = add_grhead(skb, pmc, type, &pgr, mtu);
}
}
if (pgr)
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 9828dc2..bb84165 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -964,7 +964,7 @@
tcp_rsk(req)->rcv_nxt,
req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale,
tcp_time_stamp, req->ts_recent, sk->sk_bound_dev_if,
- tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr),
+ tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr),
0, 0);
}
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c
index efa3f48..73e8f34 100644
--- a/net/mac80211/wep.c
+++ b/net/mac80211/wep.c
@@ -293,7 +293,8 @@
return RX_DROP_UNUSABLE;
ieee80211_wep_remove_iv(rx->local, rx->skb, rx->key);
/* remove ICV */
- if (pskb_trim(rx->skb, rx->skb->len - IEEE80211_WEP_ICV_LEN))
+ if (!(status->flag & RX_FLAG_ICV_STRIPPED) &&
+ pskb_trim(rx->skb, rx->skb->len - IEEE80211_WEP_ICV_LEN))
return RX_DROP_UNUSABLE;
}
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 5c71d60..caa5986 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -295,7 +295,8 @@
return RX_DROP_UNUSABLE;
/* Trim ICV */
- skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN);
+ if (!(status->flag & RX_FLAG_ICV_STRIPPED))
+ skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN);
/* Remove IV */
memmove(skb->data + IEEE80211_TKIP_IV_LEN, skb->data, hdrlen);
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 1ff497b..e1c123d 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -261,6 +261,9 @@
struct sock *sk = skb->sk;
int ret = -ENOMEM;
+ if (!net_eq(dev_net(dev), sock_net(sk)))
+ return 0;
+
dev_hold(dev);
if (is_vmalloc_addr(skb->head))
diff --git a/net/rds/rdma.c b/net/rds/rdma.c
index de8496e..f6027f4 100644
--- a/net/rds/rdma.c
+++ b/net/rds/rdma.c
@@ -524,6 +524,9 @@
local_vec = (struct rds_iovec __user *)(unsigned long) args->local_vec_addr;
+ if (args->nr_local == 0)
+ return -EINVAL;
+
/* figure out the number of pages in the vector */
for (i = 0; i < args->nr_local; i++) {
if (copy_from_user(&vec, &local_vec[i],
@@ -873,6 +876,7 @@
err:
if (page)
put_page(page);
+ rm->atomic.op_active = 0;
kfree(rm->atomic.op_notifier);
return ret;
diff --git a/net/rds/send.c b/net/rds/send.c
index ad247dc..ef53d164 100644
--- a/net/rds/send.c
+++ b/net/rds/send.c
@@ -1006,6 +1006,9 @@
continue;
if (cmsg->cmsg_type == RDS_CMSG_RDMA_ARGS) {
+ if (cmsg->cmsg_len <
+ CMSG_LEN(sizeof(struct rds_rdma_args)))
+ return -EINVAL;
args = CMSG_DATA(cmsg);
*rdma_bytes += args->remote_vec.bytes;
}
diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c
index e0aa30f..9617b42 100644
--- a/net/sched/act_gact.c
+++ b/net/sched/act_gact.c
@@ -161,7 +161,7 @@
if (action == TC_ACT_SHOT)
this_cpu_ptr(gact->common.cpu_qstats)->drops += packets;
- tm->lastuse = lastuse;
+ tm->lastuse = max_t(u64, tm->lastuse, lastuse);
}
static int tcf_gact_dump(struct sk_buff *skb, struct tc_action *a,
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index 6b07fba..fc3650b 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -211,7 +211,7 @@
struct tcf_t *tm = &m->tcf_tm;
_bstats_cpu_update(this_cpu_ptr(a->cpu_bstats), bytes, packets);
- tm->lastuse = lastuse;
+ tm->lastuse = max_t(u64, tm->lastuse, lastuse);
}
static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind,
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index c2ab864..7181ce6 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -4246,7 +4246,7 @@
SCTP_DBG_OBJCNT_INC(sock);
local_bh_disable();
- percpu_counter_inc(&sctp_sockets_allocated);
+ sk_sockets_allocated_inc(sk);
sock_prot_inuse_add(net, sk->sk_prot, 1);
/* Nothing can fail after this block, otherwise
@@ -4290,7 +4290,7 @@
}
sctp_endpoint_free(sp->ep);
local_bh_disable();
- percpu_counter_dec(&sctp_sockets_allocated);
+ sk_sockets_allocated_dec(sk);
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
local_bh_enable();
}
diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c
index 25d9a9c..624c322 100644
--- a/net/sunrpc/auth_gss/gss_rpc_xdr.c
+++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c
@@ -231,6 +231,7 @@
goto out_free_groups;
creds->cr_group_info->gid[i] = kgid;
}
+ groups_sort(creds->cr_group_info);
return 0;
out_free_groups:
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 1530825..6a08bc4 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -481,6 +481,7 @@
goto out;
rsci.cred.cr_group_info->gid[i] = kgid;
}
+ groups_sort(rsci.cred.cr_group_info);
/* mech name */
len = qword_get(&mesg, buf, mlen);
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index 64af4f0..738a243 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -520,6 +520,7 @@
ug.gi->gid[i] = kgid;
}
+ groups_sort(ug.gi);
ugp = unix_gid_lookup(cd, uid);
if (ugp) {
struct cache_head *ch;
@@ -819,6 +820,7 @@
kgid_t kgid = make_kgid(&init_user_ns, svc_getnl(argv));
cred->cr_group_info->gid[i] = kgid;
}
+ groups_sort(cred->cr_group_info);
if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) {
*authp = rpc_autherr_badverf;
return SVC_DENIED;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index d8387b1..8f9bd38 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -6701,8 +6701,17 @@
if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
request->flags = nla_get_u32(
info->attrs[NL80211_ATTR_SCAN_FLAGS]);
- if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
- !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
+ if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
+ !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
+ ((request->flags & NL80211_SCAN_FLAG_LOW_SPAN) &&
+ !wiphy_ext_feature_isset(wiphy,
+ NL80211_EXT_FEATURE_LOW_SPAN_SCAN)) ||
+ ((request->flags & NL80211_SCAN_FLAG_LOW_POWER) &&
+ !wiphy_ext_feature_isset(wiphy,
+ NL80211_EXT_FEATURE_LOW_POWER_SCAN)) ||
+ ((request->flags & NL80211_SCAN_FLAG_HIGH_ACCURACY) &&
+ !wiphy_ext_feature_isset(wiphy,
+ NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN))) {
err = -EOPNOTSUPP;
goto out_free;
}
@@ -7585,6 +7594,11 @@
intbss->ts_boottime, NL80211_BSS_PAD))
goto nla_put_failure;
+ if (!nl80211_put_signal(msg, intbss->pub.chains,
+ intbss->pub.chain_signal,
+ NL80211_BSS_CHAIN_SIGNAL))
+ goto nla_put_failure;
+
switch (rdev->wiphy.signal_type) {
case CFG80211_SIGNAL_TYPE_MBM:
if (nla_put_u32(msg, NL80211_BSS_SIGNAL_MBM, res->signal))
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 35ad69f..ad8611b 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -904,6 +904,9 @@
found->ts = tmp->ts;
found->ts_boottime = tmp->ts_boottime;
found->parent_tsf = tmp->parent_tsf;
+ found->pub.chains = tmp->pub.chains;
+ memcpy(found->pub.chain_signal, tmp->pub.chain_signal,
+ IEEE80211_MAX_CHAINS);
ether_addr_copy(found->parent_bssid, tmp->parent_bssid);
} else {
struct cfg80211_internal_bss *new;
@@ -1156,6 +1159,8 @@
tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
tmp.ts_boottime = data->boottime_ns;
tmp.parent_tsf = data->parent_tsf;
+ tmp.pub.chains = data->chains;
+ memcpy(tmp.pub.chain_signal, data->chain_signal, IEEE80211_MAX_CHAINS);
ether_addr_copy(tmp.parent_bssid, data->parent_bssid);
signal_valid = abs(data->chan->center_freq - channel->center_freq) <=
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index bd83497..845eb9b 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -838,6 +838,7 @@
".cmem*", /* EZchip */
".fmt_slot*", /* EZchip */
".gnu.lto*",
+ ".discard.*",
NULL
};
diff --git a/scripts/module-common.lds b/scripts/module-common.lds
index 53234e8..9b6e246 100644
--- a/scripts/module-common.lds
+++ b/scripts/module-common.lds
@@ -4,7 +4,10 @@
* combine them automatically.
*/
SECTIONS {
- /DISCARD/ : { *(.discard) }
+ /DISCARD/ : {
+ *(.discard)
+ *(.discard.*)
+ }
__ksymtab 0 : { *(SORT(___ksymtab+*)) }
__ksymtab_gpl 0 : { *(SORT(___ksymtab_gpl+*)) }
diff --git a/security/Kconfig b/security/Kconfig
index 4415de2..638afc8 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -50,6 +50,16 @@
bool
default n
+config PAGE_TABLE_ISOLATION
+ bool "Remove the kernel mapping in user mode"
+ default y
+ depends on X86_64 && SMP
+ help
+ This enforces a strict kernel and user space isolation, in order
+ to close hardware side channels on kernel address information.
+
+ If you are unsure how to answer this question, answer Y.
+
config SECURITYFS
bool "Enable the securityfs filesystem"
help
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index e60c79d..52f3c55 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -348,27 +348,26 @@
struct avc_xperms_decision_node *xpd_node;
struct extended_perms_decision *xpd;
- xpd_node = kmem_cache_zalloc(avc_xperms_decision_cachep,
- GFP_ATOMIC | __GFP_NOMEMALLOC);
+ xpd_node = kmem_cache_zalloc(avc_xperms_decision_cachep, GFP_NOWAIT);
if (!xpd_node)
return NULL;
xpd = &xpd_node->xpd;
if (which & XPERMS_ALLOWED) {
xpd->allowed = kmem_cache_zalloc(avc_xperms_data_cachep,
- GFP_ATOMIC | __GFP_NOMEMALLOC);
+ GFP_NOWAIT);
if (!xpd->allowed)
goto error;
}
if (which & XPERMS_AUDITALLOW) {
xpd->auditallow = kmem_cache_zalloc(avc_xperms_data_cachep,
- GFP_ATOMIC | __GFP_NOMEMALLOC);
+ GFP_NOWAIT);
if (!xpd->auditallow)
goto error;
}
if (which & XPERMS_DONTAUDIT) {
xpd->dontaudit = kmem_cache_zalloc(avc_xperms_data_cachep,
- GFP_ATOMIC | __GFP_NOMEMALLOC);
+ GFP_NOWAIT);
if (!xpd->dontaudit)
goto error;
}
@@ -396,8 +395,7 @@
{
struct avc_xperms_node *xp_node;
- xp_node = kmem_cache_zalloc(avc_xperms_cachep,
- GFP_ATOMIC|__GFP_NOMEMALLOC);
+ xp_node = kmem_cache_zalloc(avc_xperms_cachep, GFP_NOWAIT);
if (!xp_node)
return xp_node;
INIT_LIST_HEAD(&xp_node->xpd_head);
@@ -550,7 +548,7 @@
{
struct avc_node *node;
- node = kmem_cache_zalloc(avc_node_cachep, GFP_ATOMIC|__GFP_NOMEMALLOC);
+ node = kmem_cache_zalloc(avc_node_cachep, GFP_NOWAIT);
if (!node)
goto out;
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index ebc9fdf..3321348 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -466,7 +466,6 @@
v = snd_pcm_hw_param_last(pcm, params, var, dir);
else
v = snd_pcm_hw_param_first(pcm, params, var, dir);
- snd_BUG_ON(v < 0);
return v;
}
@@ -1370,8 +1369,11 @@
if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
return tmp;
- mutex_lock(&runtime->oss.params_lock);
while (bytes > 0) {
+ if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
+ tmp = -ERESTARTSYS;
+ break;
+ }
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
tmp = bytes;
if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes)
@@ -1415,14 +1417,18 @@
xfer += tmp;
if ((substream->f_flags & O_NONBLOCK) != 0 &&
tmp != runtime->oss.period_bytes)
- break;
+ tmp = -EAGAIN;
}
- }
- mutex_unlock(&runtime->oss.params_lock);
- return xfer;
-
err:
- mutex_unlock(&runtime->oss.params_lock);
+ mutex_unlock(&runtime->oss.params_lock);
+ if (tmp < 0)
+ break;
+ if (signal_pending(current)) {
+ tmp = -ERESTARTSYS;
+ break;
+ }
+ tmp = 0;
+ }
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
}
@@ -1470,8 +1476,11 @@
if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
return tmp;
- mutex_lock(&runtime->oss.params_lock);
while (bytes > 0) {
+ if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
+ tmp = -ERESTARTSYS;
+ break;
+ }
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
if (runtime->oss.buffer_used == 0) {
tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
@@ -1502,12 +1511,16 @@
bytes -= tmp;
xfer += tmp;
}
- }
- mutex_unlock(&runtime->oss.params_lock);
- return xfer;
-
err:
- mutex_unlock(&runtime->oss.params_lock);
+ mutex_unlock(&runtime->oss.params_lock);
+ if (tmp < 0)
+ break;
+ if (signal_pending(current)) {
+ tmp = -ERESTARTSYS;
+ break;
+ }
+ tmp = 0;
+ }
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
}
diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c
index 727ac44..a84a1d3 100644
--- a/sound/core/oss/pcm_plugin.c
+++ b/sound/core/oss/pcm_plugin.c
@@ -591,18 +591,26 @@
snd_pcm_sframes_t frames = size;
plugin = snd_pcm_plug_first(plug);
- while (plugin && frames > 0) {
+ while (plugin) {
+ if (frames <= 0)
+ return frames;
if ((next = plugin->next) != NULL) {
snd_pcm_sframes_t frames1 = frames;
- if (plugin->dst_frames)
+ if (plugin->dst_frames) {
frames1 = plugin->dst_frames(plugin, frames);
+ if (frames1 <= 0)
+ return frames1;
+ }
if ((err = next->client_channels(next, frames1, &dst_channels)) < 0) {
return err;
}
if (err != frames1) {
frames = err;
- if (plugin->src_frames)
+ if (plugin->src_frames) {
frames = plugin->src_frames(plugin, frames1);
+ if (frames <= 0)
+ return frames;
+ }
}
} else
dst_channels = NULL;
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 2dfe772..25deca44 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1668,7 +1668,7 @@
return changed;
if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params);
- if (snd_BUG_ON(err < 0))
+ if (err < 0)
return err;
}
return snd_pcm_hw_param_value(params, var, dir);
@@ -1715,7 +1715,7 @@
return changed;
if (params->rmask) {
int err = snd_pcm_hw_refine(pcm, params);
- if (snd_BUG_ON(err < 0))
+ if (err < 0)
return err;
}
return snd_pcm_hw_param_value(params, var, dir);
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index 54f348a..cbd20cb 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -39,6 +39,7 @@
#include <sound/core.h>
#include <sound/control.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include <sound/info.h>
#include <sound/initval.h>
@@ -305,19 +306,6 @@
return 0;
}
-static void params_change_substream(struct loopback_pcm *dpcm,
- struct snd_pcm_runtime *runtime)
-{
- struct snd_pcm_runtime *dst_runtime;
-
- if (dpcm == NULL || dpcm->substream == NULL)
- return;
- dst_runtime = dpcm->substream->runtime;
- if (dst_runtime == NULL)
- return;
- dst_runtime->hw = dpcm->cable->hw;
-}
-
static void params_change(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -329,10 +317,6 @@
cable->hw.rate_max = runtime->rate;
cable->hw.channels_min = runtime->channels;
cable->hw.channels_max = runtime->channels;
- params_change_substream(cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
- runtime);
- params_change_substream(cable->streams[SNDRV_PCM_STREAM_CAPTURE],
- runtime);
}
static int loopback_prepare(struct snd_pcm_substream *substream)
@@ -620,26 +604,29 @@
static int rule_format(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
+ struct loopback_pcm *dpcm = rule->private;
+ struct loopback_cable *cable = dpcm->cable;
+ struct snd_mask m;
- struct snd_pcm_hardware *hw = rule->private;
- struct snd_mask *maskp = hw_param_mask(params, rule->var);
-
- maskp->bits[0] &= (u_int32_t)hw->formats;
- maskp->bits[1] &= (u_int32_t)(hw->formats >> 32);
- memset(maskp->bits + 2, 0, (SNDRV_MASK_MAX-64) / 8); /* clear rest */
- if (! maskp->bits[0] && ! maskp->bits[1])
- return -EINVAL;
- return 0;
+ snd_mask_none(&m);
+ mutex_lock(&dpcm->loopback->cable_lock);
+ m.bits[0] = (u_int32_t)cable->hw.formats;
+ m.bits[1] = (u_int32_t)(cable->hw.formats >> 32);
+ mutex_unlock(&dpcm->loopback->cable_lock);
+ return snd_mask_refine(hw_param_mask(params, rule->var), &m);
}
static int rule_rate(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- struct snd_pcm_hardware *hw = rule->private;
+ struct loopback_pcm *dpcm = rule->private;
+ struct loopback_cable *cable = dpcm->cable;
struct snd_interval t;
- t.min = hw->rate_min;
- t.max = hw->rate_max;
+ mutex_lock(&dpcm->loopback->cable_lock);
+ t.min = cable->hw.rate_min;
+ t.max = cable->hw.rate_max;
+ mutex_unlock(&dpcm->loopback->cable_lock);
t.openmin = t.openmax = 0;
t.integer = 0;
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
@@ -648,22 +635,44 @@
static int rule_channels(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
- struct snd_pcm_hardware *hw = rule->private;
+ struct loopback_pcm *dpcm = rule->private;
+ struct loopback_cable *cable = dpcm->cable;
struct snd_interval t;
- t.min = hw->channels_min;
- t.max = hw->channels_max;
+ mutex_lock(&dpcm->loopback->cable_lock);
+ t.min = cable->hw.channels_min;
+ t.max = cable->hw.channels_max;
+ mutex_unlock(&dpcm->loopback->cable_lock);
t.openmin = t.openmax = 0;
t.integer = 0;
return snd_interval_refine(hw_param_interval(params, rule->var), &t);
}
+static void free_cable(struct snd_pcm_substream *substream)
+{
+ struct loopback *loopback = substream->private_data;
+ int dev = get_cable_index(substream);
+ struct loopback_cable *cable;
+
+ cable = loopback->cables[substream->number][dev];
+ if (!cable)
+ return;
+ if (cable->streams[!substream->stream]) {
+ /* other stream is still alive */
+ cable->streams[substream->stream] = NULL;
+ } else {
+ /* free the cable */
+ loopback->cables[substream->number][dev] = NULL;
+ kfree(cable);
+ }
+}
+
static int loopback_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm;
- struct loopback_cable *cable;
+ struct loopback_cable *cable = NULL;
int err = 0;
int dev = get_cable_index(substream);
@@ -682,7 +691,6 @@
if (!cable) {
cable = kzalloc(sizeof(*cable), GFP_KERNEL);
if (!cable) {
- kfree(dpcm);
err = -ENOMEM;
goto unlock;
}
@@ -700,19 +708,19 @@
/* are cached -> they do not reflect the actual state */
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_FORMAT,
- rule_format, &runtime->hw,
+ rule_format, dpcm,
SNDRV_PCM_HW_PARAM_FORMAT, -1);
if (err < 0)
goto unlock;
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
- rule_rate, &runtime->hw,
+ rule_rate, dpcm,
SNDRV_PCM_HW_PARAM_RATE, -1);
if (err < 0)
goto unlock;
err = snd_pcm_hw_rule_add(runtime, 0,
SNDRV_PCM_HW_PARAM_CHANNELS,
- rule_channels, &runtime->hw,
+ rule_channels, dpcm,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
if (err < 0)
goto unlock;
@@ -724,6 +732,10 @@
else
runtime->hw = cable->hw;
unlock:
+ if (err < 0) {
+ free_cable(substream);
+ kfree(dpcm);
+ }
mutex_unlock(&loopback->cable_lock);
return err;
}
@@ -732,20 +744,10 @@
{
struct loopback *loopback = substream->private_data;
struct loopback_pcm *dpcm = substream->runtime->private_data;
- struct loopback_cable *cable;
- int dev = get_cable_index(substream);
loopback_timer_stop(dpcm);
mutex_lock(&loopback->cable_lock);
- cable = loopback->cables[substream->number][dev];
- if (cable->streams[!substream->stream]) {
- /* other stream is still alive */
- cable->streams[substream->stream] = NULL;
- } else {
- /* free the cable */
- loopback->cables[substream->number][dev] = NULL;
- kfree(cable);
- }
+ free_cable(substream);
mutex_unlock(&loopback->cable_lock);
return 0;
}
diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c
index c9af022..47c3e97 100644
--- a/sound/hda/hdac_i915.c
+++ b/sound/hda/hdac_i915.c
@@ -319,7 +319,7 @@
*/
int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops *aops)
{
- if (WARN_ON(!hdac_acomp))
+ if (!hdac_acomp)
return -ENODEV;
hdac_acomp->audio_ops = aops;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index ba40596..4ef3b00 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -5972,6 +5972,11 @@
{0x1b, 0x01011020},
{0x21, 0x02211010}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60130},
+ {0x14, 0x90170110},
+ {0x1b, 0x01011020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60160},
{0x14, 0x90170120},
{0x21, 0x02211030}),
diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c
index c69e976..f886324 100644
--- a/sound/soc/codecs/da7218.c
+++ b/sound/soc/codecs/da7218.c
@@ -2519,7 +2519,7 @@
}
if (da7218->dev_id == DA7218_DEV_ID) {
- hpldet_np = of_find_node_by_name(np, "da7218_hpldet");
+ hpldet_np = of_get_child_by_name(np, "da7218_hpldet");
if (!hpldet_np)
return pdata;
diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h
index 5acd5b6..f9b6c5a 100644
--- a/sound/soc/codecs/tlv320aic31xx.h
+++ b/sound/soc/codecs/tlv320aic31xx.h
@@ -115,7 +115,7 @@
/* INT2 interrupt control */
#define AIC31XX_INT2CTRL AIC31XX_REG(0, 49)
/* GPIO1 control */
-#define AIC31XX_GPIO1 AIC31XX_REG(0, 50)
+#define AIC31XX_GPIO1 AIC31XX_REG(0, 51)
#define AIC31XX_DACPRB AIC31XX_REG(0, 60)
/* ADC Instruction Set Register */
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index a2104d6..26fd6a6 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -232,7 +232,7 @@
struct twl4030_codec_data *pdata = dev_get_platdata(codec->dev);
struct device_node *twl4030_codec_node = NULL;
- twl4030_codec_node = of_find_node_by_name(codec->dev->parent->of_node,
+ twl4030_codec_node = of_get_child_by_name(codec->dev->parent->of_node,
"codec");
if (!pdata && twl4030_codec_node) {
@@ -241,9 +241,11 @@
GFP_KERNEL);
if (!pdata) {
dev_err(codec->dev, "Can not allocate memory\n");
+ of_node_put(twl4030_codec_node);
return NULL;
}
twl4030_setup_pdata_of(pdata, twl4030_codec_node);
+ of_node_put(twl4030_codec_node);
}
return pdata;
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 757af79..c03c9da 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -1465,7 +1465,7 @@
le64_to_cpu(footer->timestamp));
while (pos < firmware->size &&
- pos - firmware->size > sizeof(*region)) {
+ sizeof(*region) < firmware->size - pos) {
region = (void *)&(firmware->data[pos]);
region_name = "Unknown";
reg = 0;
@@ -1526,8 +1526,8 @@
regions, le32_to_cpu(region->len), offset,
region_name);
- if ((pos + le32_to_cpu(region->len) + sizeof(*region)) >
- firmware->size) {
+ if (le32_to_cpu(region->len) >
+ firmware->size - pos - sizeof(*region)) {
adsp_err(dsp,
"%s.%d: %s region len %d bytes exceeds file length %zu\n",
file, regions, region_name,
@@ -1992,7 +1992,7 @@
blocks = 0;
while (pos < firmware->size &&
- pos - firmware->size > sizeof(*blk)) {
+ sizeof(*blk) < firmware->size - pos) {
blk = (void *)(&firmware->data[pos]);
type = le16_to_cpu(blk->type);
@@ -2066,8 +2066,8 @@
}
if (reg) {
- if ((pos + le32_to_cpu(blk->len) + sizeof(*blk)) >
- firmware->size) {
+ if (le32_to_cpu(blk->len) >
+ firmware->size - pos - sizeof(*blk)) {
adsp_err(dsp,
"%s.%d: %s region len %d bytes exceeds file length %zu\n",
file, blocks, region_name,
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index fde08660..1c03490 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -1467,12 +1467,6 @@
sizeof(fsl_ssi_ac97_dai));
fsl_ac97_data = ssi_private;
-
- ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
- if (ret) {
- dev_err(&pdev->dev, "could not set AC'97 ops\n");
- return ret;
- }
} else {
/* Initialize this copy of the CPU DAI driver structure */
memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
@@ -1583,6 +1577,14 @@
return ret;
}
+ if (fsl_ssi_is_ac97(ssi_private)) {
+ ret = snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "could not set AC'97 ops\n");
+ goto error_ac97_ops;
+ }
+ }
+
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
&ssi_private->cpu_dai_drv, 1);
if (ret) {
@@ -1666,6 +1668,10 @@
fsl_ssi_debugfs_remove(&ssi_private->dbg_stats);
error_asoc_register:
+ if (fsl_ssi_is_ac97(ssi_private))
+ snd_soc_set_ac97_ops(NULL);
+
+error_ac97_ops:
if (ssi_private->soc->imx)
fsl_ssi_imx_clean(pdev, ssi_private);
diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h
index a396292..f79669a 100644
--- a/tools/arch/x86/include/asm/cpufeatures.h
+++ b/tools/arch/x86/include/asm/cpufeatures.h
@@ -197,6 +197,9 @@
#define X86_FEATURE_AVX512_4VNNIW (7*32+16) /* AVX-512 Neural Network Instructions */
#define X86_FEATURE_AVX512_4FMAPS (7*32+17) /* AVX-512 Multiply Accumulation Single precision */
+/* Because the ALTERNATIVE scheme is for members of the X86_FEATURE club... */
+#define X86_FEATURE_KAISER ( 7*32+31) /* CONFIG_PAGE_TABLE_ISOLATION w/o nokaiser */
+
/* Virtualization flags: Linux defined, word 8 */
#define X86_FEATURE_TPR_SHADOW ( 8*32+ 0) /* Intel TPR Shadow */
#define X86_FEATURE_VNMI ( 8*32+ 1) /* Intel Virtual NMI */
diff --git a/tools/objtool/arch/x86/insn/x86-opcode-map.txt b/tools/objtool/arch/x86/insn/x86-opcode-map.txt
index 767be7c..1754e09 100644
--- a/tools/objtool/arch/x86/insn/x86-opcode-map.txt
+++ b/tools/objtool/arch/x86/insn/x86-opcode-map.txt
@@ -896,7 +896,7 @@
GrpTable: Grp3_1
0: TEST Eb,Ib
-1:
+1: TEST Eb,Ib
2: NOT Eb
3: NEG Eb
4: MUL AL,Eb
diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index b8dadb0..a688a85 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -51,7 +51,7 @@
unsigned int len, state;
unsigned char type;
unsigned long immediate;
- bool alt_group, visited;
+ bool alt_group, visited, ignore_alts;
struct symbol *call_dest;
struct instruction *jump_dest;
struct list_head alts;
@@ -353,6 +353,40 @@
}
/*
+ * FIXME: For now, just ignore any alternatives which add retpolines. This is
+ * a temporary hack, as it doesn't allow ORC to unwind from inside a retpoline.
+ * But it at least allows objtool to understand the control flow *around* the
+ * retpoline.
+ */
+static int add_nospec_ignores(struct objtool_file *file)
+{
+ struct section *sec;
+ struct rela *rela;
+ struct instruction *insn;
+
+ sec = find_section_by_name(file->elf, ".rela.discard.nospec");
+ if (!sec)
+ return 0;
+
+ list_for_each_entry(rela, &sec->rela_list, list) {
+ if (rela->sym->type != STT_SECTION) {
+ WARN("unexpected relocation symbol type in %s", sec->name);
+ return -1;
+ }
+
+ insn = find_insn(file, rela->sym->sec, rela->addend);
+ if (!insn) {
+ WARN("bad .discard.nospec entry");
+ return -1;
+ }
+
+ insn->ignore_alts = true;
+ }
+
+ return 0;
+}
+
+/*
* Find the destination instructions for all jumps.
*/
static int add_jump_destinations(struct objtool_file *file)
@@ -382,6 +416,13 @@
} else if (rela->sym->sec->idx) {
dest_sec = rela->sym->sec;
dest_off = rela->sym->sym.st_value + rela->addend + 4;
+ } else if (strstr(rela->sym->name, "_indirect_thunk_")) {
+ /*
+ * Retpoline jumps are really dynamic jumps in
+ * disguise, so convert them accordingly.
+ */
+ insn->type = INSN_JUMP_DYNAMIC;
+ continue;
} else {
/* sibling call */
insn->jump_dest = 0;
@@ -428,11 +469,18 @@
dest_off = insn->offset + insn->len + insn->immediate;
insn->call_dest = find_symbol_by_offset(insn->sec,
dest_off);
+ /*
+ * FIXME: Thanks to retpolines, it's now considered
+ * normal for a function to call within itself. So
+ * disable this warning for now.
+ */
+#if 0
if (!insn->call_dest) {
WARN_FUNC("can't find call dest symbol at offset 0x%lx",
insn->sec, insn->offset, dest_off);
return -1;
}
+#endif
} else if (rela->sym->type == STT_SECTION) {
insn->call_dest = find_symbol_by_offset(rela->sym->sec,
rela->addend+4);
@@ -594,12 +642,6 @@
return ret;
list_for_each_entry_safe(special_alt, tmp, &special_alts, list) {
- alt = malloc(sizeof(*alt));
- if (!alt) {
- WARN("malloc failed");
- ret = -1;
- goto out;
- }
orig_insn = find_insn(file, special_alt->orig_sec,
special_alt->orig_off);
@@ -610,6 +652,10 @@
goto out;
}
+ /* Ignore retpoline alternatives. */
+ if (orig_insn->ignore_alts)
+ continue;
+
new_insn = NULL;
if (!special_alt->group || special_alt->new_len) {
new_insn = find_insn(file, special_alt->new_sec,
@@ -635,6 +681,13 @@
goto out;
}
+ alt = malloc(sizeof(*alt));
+ if (!alt) {
+ WARN("malloc failed");
+ ret = -1;
+ goto out;
+ }
+
alt->insn = new_insn;
list_add_tail(&alt->list, &orig_insn->alts);
@@ -854,6 +907,10 @@
add_ignores(file);
+ ret = add_nospec_ignores(file);
+ if (ret)
+ return ret;
+
ret = add_jump_destinations(file);
if (ret)
return ret;
@@ -1173,6 +1230,14 @@
for_each_insn(file, insn) {
if (!insn->visited && insn->type == INSN_RETURN) {
+
+ /*
+ * Don't warn about call instructions in unvisited
+ * retpoline alternatives.
+ */
+ if (!strcmp(insn->sec->name, ".altinstr_replacement"))
+ continue;
+
WARN_FUNC("return instruction outside of a callable function",
insn->sec, insn->offset);
warnings++;
@@ -1229,7 +1294,7 @@
INIT_LIST_HEAD(&file.insn_list);
hash_init(file.insn_hash);
- file.whitelist = find_section_by_name(file.elf, "__func_stack_frame_non_standard");
+ file.whitelist = find_section_by_name(file.elf, ".discard.func_stack_frame_non_standard");
file.rodata = find_section_by_name(file.elf, ".rodata");
file.ignore_unreachables = false;
file.c_file = find_section_by_name(file.elf, ".comment");
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile
index 6300c1a..4af37bf 100644
--- a/tools/testing/selftests/x86/Makefile
+++ b/tools/testing/selftests/x86/Makefile
@@ -6,7 +6,7 @@
TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_syscall test_mremap_vdso \
check_initial_reg_state sigreturn ldt_gdt iopl mpx-mini-test \
- protection_keys
+ protection_keys test_vsyscall
TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \
test_FCMOV test_FCOMI test_FISTTP \
vdso_restorer
diff --git a/tools/testing/selftests/x86/test_vsyscall.c b/tools/testing/selftests/x86/test_vsyscall.c
new file mode 100644
index 0000000..6e0bd52
--- /dev/null
+++ b/tools/testing/selftests/x86/test_vsyscall.c
@@ -0,0 +1,500 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <sys/ucontext.h>
+#include <errno.h>
+#include <err.h>
+#include <sched.h>
+#include <stdbool.h>
+#include <setjmp.h>
+
+#ifdef __x86_64__
+# define VSYS(x) (x)
+#else
+# define VSYS(x) 0
+#endif
+
+#ifndef SYS_getcpu
+# ifdef __x86_64__
+# define SYS_getcpu 309
+# else
+# define SYS_getcpu 318
+# endif
+#endif
+
+static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
+ int flags)
+{
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = handler;
+ sa.sa_flags = SA_SIGINFO | flags;
+ sigemptyset(&sa.sa_mask);
+ if (sigaction(sig, &sa, 0))
+ err(1, "sigaction");
+}
+
+/* vsyscalls and vDSO */
+bool should_read_vsyscall = false;
+
+typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz);
+gtod_t vgtod = (gtod_t)VSYS(0xffffffffff600000);
+gtod_t vdso_gtod;
+
+typedef int (*vgettime_t)(clockid_t, struct timespec *);
+vgettime_t vdso_gettime;
+
+typedef long (*time_func_t)(time_t *t);
+time_func_t vtime = (time_func_t)VSYS(0xffffffffff600400);
+time_func_t vdso_time;
+
+typedef long (*getcpu_t)(unsigned *, unsigned *, void *);
+getcpu_t vgetcpu = (getcpu_t)VSYS(0xffffffffff600800);
+getcpu_t vdso_getcpu;
+
+static void init_vdso(void)
+{
+ void *vdso = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
+ if (!vdso)
+ vdso = dlopen("linux-gate.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
+ if (!vdso) {
+ printf("[WARN]\tfailed to find vDSO\n");
+ return;
+ }
+
+ vdso_gtod = (gtod_t)dlsym(vdso, "__vdso_gettimeofday");
+ if (!vdso_gtod)
+ printf("[WARN]\tfailed to find gettimeofday in vDSO\n");
+
+ vdso_gettime = (vgettime_t)dlsym(vdso, "__vdso_clock_gettime");
+ if (!vdso_gettime)
+ printf("[WARN]\tfailed to find clock_gettime in vDSO\n");
+
+ vdso_time = (time_func_t)dlsym(vdso, "__vdso_time");
+ if (!vdso_time)
+ printf("[WARN]\tfailed to find time in vDSO\n");
+
+ vdso_getcpu = (getcpu_t)dlsym(vdso, "__vdso_getcpu");
+ if (!vdso_getcpu) {
+ /* getcpu() was never wired up in the 32-bit vDSO. */
+ printf("[%s]\tfailed to find getcpu in vDSO\n",
+ sizeof(long) == 8 ? "WARN" : "NOTE");
+ }
+}
+
+static int init_vsys(void)
+{
+#ifdef __x86_64__
+ int nerrs = 0;
+ FILE *maps;
+ char line[128];
+ bool found = false;
+
+ maps = fopen("/proc/self/maps", "r");
+ if (!maps) {
+ printf("[WARN]\tCould not open /proc/self/maps -- assuming vsyscall is r-x\n");
+ should_read_vsyscall = true;
+ return 0;
+ }
+
+ while (fgets(line, sizeof(line), maps)) {
+ char r, x;
+ void *start, *end;
+ char name[128];
+ if (sscanf(line, "%p-%p %c-%cp %*x %*x:%*x %*u %s",
+ &start, &end, &r, &x, name) != 5)
+ continue;
+
+ if (strcmp(name, "[vsyscall]"))
+ continue;
+
+ printf("\tvsyscall map: %s", line);
+
+ if (start != (void *)0xffffffffff600000 ||
+ end != (void *)0xffffffffff601000) {
+ printf("[FAIL]\taddress range is nonsense\n");
+ nerrs++;
+ }
+
+ printf("\tvsyscall permissions are %c-%c\n", r, x);
+ should_read_vsyscall = (r == 'r');
+ if (x != 'x') {
+ vgtod = NULL;
+ vtime = NULL;
+ vgetcpu = NULL;
+ }
+
+ found = true;
+ break;
+ }
+
+ fclose(maps);
+
+ if (!found) {
+ printf("\tno vsyscall map in /proc/self/maps\n");
+ should_read_vsyscall = false;
+ vgtod = NULL;
+ vtime = NULL;
+ vgetcpu = NULL;
+ }
+
+ return nerrs;
+#else
+ return 0;
+#endif
+}
+
+/* syscalls */
+static inline long sys_gtod(struct timeval *tv, struct timezone *tz)
+{
+ return syscall(SYS_gettimeofday, tv, tz);
+}
+
+static inline int sys_clock_gettime(clockid_t id, struct timespec *ts)
+{
+ return syscall(SYS_clock_gettime, id, ts);
+}
+
+static inline long sys_time(time_t *t)
+{
+ return syscall(SYS_time, t);
+}
+
+static inline long sys_getcpu(unsigned * cpu, unsigned * node,
+ void* cache)
+{
+ return syscall(SYS_getcpu, cpu, node, cache);
+}
+
+static jmp_buf jmpbuf;
+
+static void sigsegv(int sig, siginfo_t *info, void *ctx_void)
+{
+ siglongjmp(jmpbuf, 1);
+}
+
+static double tv_diff(const struct timeval *a, const struct timeval *b)
+{
+ return (double)(a->tv_sec - b->tv_sec) +
+ (double)((int)a->tv_usec - (int)b->tv_usec) * 1e-6;
+}
+
+static int check_gtod(const struct timeval *tv_sys1,
+ const struct timeval *tv_sys2,
+ const struct timezone *tz_sys,
+ const char *which,
+ const struct timeval *tv_other,
+ const struct timezone *tz_other)
+{
+ int nerrs = 0;
+ double d1, d2;
+
+ if (tz_other && (tz_sys->tz_minuteswest != tz_other->tz_minuteswest || tz_sys->tz_dsttime != tz_other->tz_dsttime)) {
+ printf("[FAIL] %s tz mismatch\n", which);
+ nerrs++;
+ }
+
+ d1 = tv_diff(tv_other, tv_sys1);
+ d2 = tv_diff(tv_sys2, tv_other);
+ printf("\t%s time offsets: %lf %lf\n", which, d1, d2);
+
+ if (d1 < 0 || d2 < 0) {
+ printf("[FAIL]\t%s time was inconsistent with the syscall\n", which);
+ nerrs++;
+ } else {
+ printf("[OK]\t%s gettimeofday()'s timeval was okay\n", which);
+ }
+
+ return nerrs;
+}
+
+static int test_gtod(void)
+{
+ struct timeval tv_sys1, tv_sys2, tv_vdso, tv_vsys;
+ struct timezone tz_sys, tz_vdso, tz_vsys;
+ long ret_vdso = -1;
+ long ret_vsys = -1;
+ int nerrs = 0;
+
+ printf("[RUN]\ttest gettimeofday()\n");
+
+ if (sys_gtod(&tv_sys1, &tz_sys) != 0)
+ err(1, "syscall gettimeofday");
+ if (vdso_gtod)
+ ret_vdso = vdso_gtod(&tv_vdso, &tz_vdso);
+ if (vgtod)
+ ret_vsys = vgtod(&tv_vsys, &tz_vsys);
+ if (sys_gtod(&tv_sys2, &tz_sys) != 0)
+ err(1, "syscall gettimeofday");
+
+ if (vdso_gtod) {
+ if (ret_vdso == 0) {
+ nerrs += check_gtod(&tv_sys1, &tv_sys2, &tz_sys, "vDSO", &tv_vdso, &tz_vdso);
+ } else {
+ printf("[FAIL]\tvDSO gettimeofday() failed: %ld\n", ret_vdso);
+ nerrs++;
+ }
+ }
+
+ if (vgtod) {
+ if (ret_vsys == 0) {
+ nerrs += check_gtod(&tv_sys1, &tv_sys2, &tz_sys, "vsyscall", &tv_vsys, &tz_vsys);
+ } else {
+ printf("[FAIL]\tvsys gettimeofday() failed: %ld\n", ret_vsys);
+ nerrs++;
+ }
+ }
+
+ return nerrs;
+}
+
+static int test_time(void) {
+ int nerrs = 0;
+
+ printf("[RUN]\ttest time()\n");
+ long t_sys1, t_sys2, t_vdso = 0, t_vsys = 0;
+ long t2_sys1 = -1, t2_sys2 = -1, t2_vdso = -1, t2_vsys = -1;
+ t_sys1 = sys_time(&t2_sys1);
+ if (vdso_time)
+ t_vdso = vdso_time(&t2_vdso);
+ if (vtime)
+ t_vsys = vtime(&t2_vsys);
+ t_sys2 = sys_time(&t2_sys2);
+ if (t_sys1 < 0 || t_sys1 != t2_sys1 || t_sys2 < 0 || t_sys2 != t2_sys2) {
+ printf("[FAIL]\tsyscall failed (ret1:%ld output1:%ld ret2:%ld output2:%ld)\n", t_sys1, t2_sys1, t_sys2, t2_sys2);
+ nerrs++;
+ return nerrs;
+ }
+
+ if (vdso_time) {
+ if (t_vdso < 0 || t_vdso != t2_vdso) {
+ printf("[FAIL]\tvDSO failed (ret:%ld output:%ld)\n", t_vdso, t2_vdso);
+ nerrs++;
+ } else if (t_vdso < t_sys1 || t_vdso > t_sys2) {
+ printf("[FAIL]\tvDSO returned the wrong time (%ld %ld %ld)\n", t_sys1, t_vdso, t_sys2);
+ nerrs++;
+ } else {
+ printf("[OK]\tvDSO time() is okay\n");
+ }
+ }
+
+ if (vtime) {
+ if (t_vsys < 0 || t_vsys != t2_vsys) {
+ printf("[FAIL]\tvsyscall failed (ret:%ld output:%ld)\n", t_vsys, t2_vsys);
+ nerrs++;
+ } else if (t_vsys < t_sys1 || t_vsys > t_sys2) {
+ printf("[FAIL]\tvsyscall returned the wrong time (%ld %ld %ld)\n", t_sys1, t_vsys, t_sys2);
+ nerrs++;
+ } else {
+ printf("[OK]\tvsyscall time() is okay\n");
+ }
+ }
+
+ return nerrs;
+}
+
+static int test_getcpu(int cpu)
+{
+ int nerrs = 0;
+ long ret_sys, ret_vdso = -1, ret_vsys = -1;
+
+ printf("[RUN]\tgetcpu() on CPU %d\n", cpu);
+
+ cpu_set_t cpuset;
+ CPU_ZERO(&cpuset);
+ CPU_SET(cpu, &cpuset);
+ if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) {
+ printf("[SKIP]\tfailed to force CPU %d\n", cpu);
+ return nerrs;
+ }
+
+ unsigned cpu_sys, cpu_vdso, cpu_vsys, node_sys, node_vdso, node_vsys;
+ unsigned node = 0;
+ bool have_node = false;
+ ret_sys = sys_getcpu(&cpu_sys, &node_sys, 0);
+ if (vdso_getcpu)
+ ret_vdso = vdso_getcpu(&cpu_vdso, &node_vdso, 0);
+ if (vgetcpu)
+ ret_vsys = vgetcpu(&cpu_vsys, &node_vsys, 0);
+
+ if (ret_sys == 0) {
+ if (cpu_sys != cpu) {
+ printf("[FAIL]\tsyscall reported CPU %hu but should be %d\n", cpu_sys, cpu);
+ nerrs++;
+ }
+
+ have_node = true;
+ node = node_sys;
+ }
+
+ if (vdso_getcpu) {
+ if (ret_vdso) {
+ printf("[FAIL]\tvDSO getcpu() failed\n");
+ nerrs++;
+ } else {
+ if (!have_node) {
+ have_node = true;
+ node = node_vdso;
+ }
+
+ if (cpu_vdso != cpu) {
+ printf("[FAIL]\tvDSO reported CPU %hu but should be %d\n", cpu_vdso, cpu);
+ nerrs++;
+ } else {
+ printf("[OK]\tvDSO reported correct CPU\n");
+ }
+
+ if (node_vdso != node) {
+ printf("[FAIL]\tvDSO reported node %hu but should be %hu\n", node_vdso, node);
+ nerrs++;
+ } else {
+ printf("[OK]\tvDSO reported correct node\n");
+ }
+ }
+ }
+
+ if (vgetcpu) {
+ if (ret_vsys) {
+ printf("[FAIL]\tvsyscall getcpu() failed\n");
+ nerrs++;
+ } else {
+ if (!have_node) {
+ have_node = true;
+ node = node_vsys;
+ }
+
+ if (cpu_vsys != cpu) {
+ printf("[FAIL]\tvsyscall reported CPU %hu but should be %d\n", cpu_vsys, cpu);
+ nerrs++;
+ } else {
+ printf("[OK]\tvsyscall reported correct CPU\n");
+ }
+
+ if (node_vsys != node) {
+ printf("[FAIL]\tvsyscall reported node %hu but should be %hu\n", node_vsys, node);
+ nerrs++;
+ } else {
+ printf("[OK]\tvsyscall reported correct node\n");
+ }
+ }
+ }
+
+ return nerrs;
+}
+
+static int test_vsys_r(void)
+{
+#ifdef __x86_64__
+ printf("[RUN]\tChecking read access to the vsyscall page\n");
+ bool can_read;
+ if (sigsetjmp(jmpbuf, 1) == 0) {
+ *(volatile int *)0xffffffffff600000;
+ can_read = true;
+ } else {
+ can_read = false;
+ }
+
+ if (can_read && !should_read_vsyscall) {
+ printf("[FAIL]\tWe have read access, but we shouldn't\n");
+ return 1;
+ } else if (!can_read && should_read_vsyscall) {
+ printf("[FAIL]\tWe don't have read access, but we should\n");
+ return 1;
+ } else {
+ printf("[OK]\tgot expected result\n");
+ }
+#endif
+
+ return 0;
+}
+
+
+#ifdef __x86_64__
+#define X86_EFLAGS_TF (1UL << 8)
+static volatile sig_atomic_t num_vsyscall_traps;
+
+static unsigned long get_eflags(void)
+{
+ unsigned long eflags;
+ asm volatile ("pushfq\n\tpopq %0" : "=rm" (eflags));
+ return eflags;
+}
+
+static void set_eflags(unsigned long eflags)
+{
+ asm volatile ("pushq %0\n\tpopfq" : : "rm" (eflags) : "flags");
+}
+
+static void sigtrap(int sig, siginfo_t *info, void *ctx_void)
+{
+ ucontext_t *ctx = (ucontext_t *)ctx_void;
+ unsigned long ip = ctx->uc_mcontext.gregs[REG_RIP];
+
+ if (((ip ^ 0xffffffffff600000UL) & ~0xfffUL) == 0)
+ num_vsyscall_traps++;
+}
+
+static int test_native_vsyscall(void)
+{
+ time_t tmp;
+ bool is_native;
+
+ if (!vtime)
+ return 0;
+
+ printf("[RUN]\tchecking for native vsyscall\n");
+ sethandler(SIGTRAP, sigtrap, 0);
+ set_eflags(get_eflags() | X86_EFLAGS_TF);
+ vtime(&tmp);
+ set_eflags(get_eflags() & ~X86_EFLAGS_TF);
+
+ /*
+ * If vsyscalls are emulated, we expect a single trap in the
+ * vsyscall page -- the call instruction will trap with RIP
+ * pointing to the entry point before emulation takes over.
+ * In native mode, we expect two traps, since whatever code
+ * the vsyscall page contains will be more than just a ret
+ * instruction.
+ */
+ is_native = (num_vsyscall_traps > 1);
+
+ printf("\tvsyscalls are %s (%d instructions in vsyscall page)\n",
+ (is_native ? "native" : "emulated"),
+ (int)num_vsyscall_traps);
+
+ return 0;
+}
+#endif
+
+int main(int argc, char **argv)
+{
+ int nerrs = 0;
+
+ init_vdso();
+ nerrs += init_vsys();
+
+ nerrs += test_gtod();
+ nerrs += test_time();
+ nerrs += test_getcpu(0);
+ nerrs += test_getcpu(1);
+
+ sethandler(SIGSEGV, sigsegv, 0);
+ nerrs += test_vsys_r();
+
+#ifdef __x86_64__
+ nerrs += test_native_vsyscall();
+#endif
+
+ return nerrs ? 1 : 0;
+}
diff --git a/tools/usb/usbip/src/utils.c b/tools/usb/usbip/src/utils.c
index 2b3d6d2..3d7b42e 100644
--- a/tools/usb/usbip/src/utils.c
+++ b/tools/usb/usbip/src/utils.c
@@ -30,6 +30,7 @@
char command[SYSFS_BUS_ID_SIZE + 4];
char match_busid_attr_path[SYSFS_PATH_MAX];
int rc;
+ int cmd_size;
snprintf(match_busid_attr_path, sizeof(match_busid_attr_path),
"%s/%s/%s/%s/%s/%s", SYSFS_MNT_PATH, SYSFS_BUS_NAME,
@@ -37,12 +38,14 @@
attr_name);
if (add)
- snprintf(command, SYSFS_BUS_ID_SIZE + 4, "add %s", busid);
+ cmd_size = snprintf(command, SYSFS_BUS_ID_SIZE + 4, "add %s",
+ busid);
else
- snprintf(command, SYSFS_BUS_ID_SIZE + 4, "del %s", busid);
+ cmd_size = snprintf(command, SYSFS_BUS_ID_SIZE + 4, "del %s",
+ busid);
rc = write_sysfs_attribute(match_busid_attr_path, command,
- sizeof(command));
+ cmd_size);
if (rc < 0) {
dbg("failed to write match_busid: %s", strerror(errno));
return -1;