diff --git a/Documentation/devicetree/bindings/platform/msm/ssm.txt b/Documentation/devicetree/bindings/platform/msm/ssm.txt deleted file mode 100644 index 7df3efd66577..000000000000 --- a/Documentation/devicetree/bindings/platform/msm/ssm.txt +++ /dev/null @@ -1,13 +0,0 @@ -* Qualcomm Technologies, Inc. Secure Service Module driver - -This module enables framework to which a client can register itself -specifying different attributes and defining various permission levels -associated with different combination of attribute values and mode of the system. - -Required properties: -- compatible: Must be "qcom,ssm" - -Example: - qcom,ssm { - compatible = "qcom,ssm"; - }; \ No newline at end of file diff --git a/arch/arm/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi b/arch/arm/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi new file mode 100644 index 000000000000..2bb0f6b5b116 --- /dev/null +++ b/arch/arm/boot/dts/qcom/dsi-panel-hx8399c-fhd-plus-video.dtsi @@ -0,0 +1,132 @@ +/* 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. + */ + +&mdss_mdp { + dsi_hx8399c_truly_vid: qcom,mdss_dsi_hx8399_truly_fhd_video { + qcom,mdss-dsi-panel-name = + "hx8399c video mode dsi truly panel"; + qcom,mdss-dsi-panel-type = "dsi_video_mode"; + qcom,mdss-dsi-panel-framerate = <60>; + qcom,mdss-dsi-virtual-channel-id = <0>; + qcom,mdss-dsi-stream = <0>; + qcom,mdss-dsi-panel-width = <1080>; + qcom,mdss-dsi-panel-height = <2160>; + qcom,mdss-dsi-h-front-porch = <42>; + qcom,mdss-dsi-h-back-porch = <42>; + qcom,mdss-dsi-h-pulse-width = <10>; + qcom,mdss-dsi-h-sync-skew = <0>; + qcom,mdss-dsi-v-back-porch = <15>; + qcom,mdss-dsi-v-front-porch = <10>; + qcom,mdss-dsi-v-pulse-width = <3>; + qcom,mdss-dsi-h-left-border = <0>; + qcom,mdss-dsi-h-right-border = <0>; + qcom,mdss-dsi-v-top-border = <0>; + qcom,mdss-dsi-v-bottom-border = <0>; + qcom,mdss-pan-physical-width-dimension = <65>; + qcom,mdss-pan-physical-height-dimension = <129>; + qcom,mdss-dsi-bpp = <24>; + qcom,mdss-dsi-color-order = "rgb_swap_rgb"; + qcom,mdss-dsi-underflow-color = <0xff>; + qcom,mdss-dsi-border-color = <0>; + qcom,mdss-dsi-on-command = [ + 39 01 00 00 00 00 04 + b9 ff 83 99 + 39 01 00 00 00 00 02 + d2 88 + 39 01 00 00 00 00 0c + b1 02 04 72 92 01 + 32 aa 11 11 52 57 + 39 01 00 00 00 00 10 + b2 00 80 80 cc 05 07 5a + 11 10 10 00 1e 70 03 d4 + 39 01 00 00 00 00 2d + b4 00 ff 59 59 01 ab 00 + 00 09 00 03 05 00 28 03 + 0b 0d 21 03 02 00 0c a3 + 80 59 59 02 ab 00 00 09 + 00 03 05 00 28 03 0b 0d + 02 00 0c a3 01 + 39 01 00 00 05 00 22 + d3 00 0c 03 03 00 00 10 + 10 00 00 03 00 03 00 08 + 78 08 78 00 00 00 00 00 + 24 02 05 05 03 00 00 00 + 05 40 + 39 01 00 00 05 00 21 + d5 20 20 19 19 18 18 02 + 03 00 01 24 24 18 18 18 + 18 24 24 00 00 00 00 00 + 00 00 00 2f 2f 30 30 31 + 31 + 39 01 00 00 05 00 21 + d6 24 24 18 18 19 19 01 + 00 03 02 24 24 18 18 18 + 18 20 20 40 40 40 40 40 + 40 40 40 2f 2f 30 30 31 + 31 + 39 01 00 00 00 00 02 + bd 00 + 39 01 00 00 00 00 11 + d8 aa aa aa aa aa aa aa + aa aa ba aa aa aa ba aa + aa + 39 01 00 00 00 00 02 + bd 01 + 39 01 00 00 00 00 11 + d8 00 00 00 00 00 00 00 + 00 82 ea aa aa 82 ea aa + aa + 39 01 00 00 00 00 02 + bd 02 + 39 01 00 00 00 00 09 + d8 ff ff c0 3f ff ff c0 + 3f + 39 01 00 00 00 00 02 + bd 00 + 39 01 00 00 05 00 37 + e0 01 21 31 2d 66 6f 7b + 75 7a 81 86 89 8c 90 95 + 97 9a a1 a2 aa 9e ad b0 + 5b 57 63 7a 01 21 31 2d + 66 6f 7b 75 7a 81 86 89 + 8c 90 95 97 9a a1 a2 aa + 9e ad b0 5b 57 63 7a + 39 01 00 00 00 00 03 + b6 7e 7e + 39 01 00 00 00 00 02 + cc 08 + 05 01 00 00 96 00 02 11 00 + 05 01 00 00 32 00 02 29 00]; + qcom,mdss-dsi-off-command = [ + 05 01 00 00 32 00 02 28 00 + 05 01 00 00 96 00 02 10 00]; + qcom,mdss-dsi-on-command-state = "dsi_lp_mode"; + qcom,mdss-dsi-off-command-state = "dsi_hs_mode"; + qcom,mdss-dsi-h-sync-pulse = <0>; + qcom,mdss-dsi-traffic-mode = "non_burst_sync_event"; + qcom,mdss-dsi-lane-map = "lane_map_0123"; + qcom,mdss-dsi-bllp-eof-power-mode; + qcom,mdss-dsi-bllp-power-mode; + qcom,mdss-dsi-tx-eot-append; + qcom,mdss-dsi-lane-0-state; + qcom,mdss-dsi-lane-1-state; + qcom,mdss-dsi-lane-2-state; + qcom,mdss-dsi-lane-3-state; + qcom,mdss-dsi-t-clk-post = <0x0e>; + qcom,mdss-dsi-t-clk-pre = <0x31>; + qcom,mdss-dsi-dma-trigger = "trigger_sw"; + qcom,mdss-dsi-mdp-trigger = "none"; + qcom,mdss-dsi-lp11-init; + qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>; + }; +}; + diff --git a/arch/arm/boot/dts/qcom/sdm630.dtsi b/arch/arm/boot/dts/qcom/sdm630.dtsi index 352e872c631f..d61dc20dbb15 100644 --- a/arch/arm/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630.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 @@ -1677,7 +1677,7 @@ vdd-1.8-xo-supply = <&pm660_l9_pin_ctrl>; vdd-1.3-rfa-supply = <&pm660_l6_pin_ctrl>; vdd-3.3-ch0-supply = <&pm660_l19_pin_ctrl>; - qcom,vdd-0.8-cx-mx-config = <525000 950000>; + qcom,vdd-0.8-cx-mx-config = <848000 848000>; qcom,vdd-1.8-xo-config = <1750000 1900000>; qcom,vdd-1.3-rfa-config = <1200000 1370000>; qcom,vdd-3.3-ch0-config = <3200000 3400000>; diff --git a/arch/arm/boot/dts/qcom/sdm636.dtsi b/arch/arm/boot/dts/qcom/sdm636.dtsi index 8250a2a46453..e053e191e982 100644 --- a/arch/arm/boot/dts/qcom/sdm636.dtsi +++ b/arch/arm/boot/dts/qcom/sdm636.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 @@ -71,3 +71,9 @@ }; }; }; + +/* GPU overrides */ +&msm_gpu { + /* Update GPU chip ID*/ + qcom,chipid = <0x05000900>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi b/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi index 7db93928a369..70fc57e2594f 100644 --- a/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-cdp.dtsi @@ -191,6 +191,13 @@ qcom,panel-supply-entries = <&dsi_panel_pwr_supply>; }; +&dsi_hx8399c_truly_vid { + 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,panel-supply-entries = <&dsi_panel_pwr_supply>; +}; + &mdss_dp_ctrl { pinctrl-names = "mdss_dp_active", "mdss_dp_sleep"; pinctrl-0 = <&mdss_dp_aux_active &mdss_dp_usbplug_cc_active>; diff --git a/arch/arm/boot/dts/qcom/sdm660-fhd-cdp.dts b/arch/arm/boot/dts/qcom/sdm660-fhd-cdp.dts new file mode 100644 index 000000000000..3bbe1669341b --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm660-fhd-cdp.dts @@ -0,0 +1,53 @@ +/* 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. + */ + + +/dts-v1/; + +#include "sdm660.dtsi" +#include "sdm660-cdp.dtsi" +#include "sdm660-internal-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 660 PM660 + PM660L Int. FHD Display"; + compatible = "qcom,sdm660-cdp", "qcom,sdm660", "qcom,cdp"; + qcom,board-id = <1 3>; + qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>, + <0x0001001b 0x0201011a 0x0 0x0>; +}; + +&mdss_mdp { + qcom,mdss-pref-prim-intf = "dsi"; +}; + +&mdss_dsi { + hw-config = "single_dsi"; +}; + +&mdss_dsi0 { + qcom,dsi-pref-prim-pan = <&dsi_nt35695b_truly_fhd_video>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 53 0>; + qcom,platform-te-gpio = <&tlmm 59 0>; +}; + +&mdss_dsi1 { + status = "disabled"; + qcom,dsi-pref-prim-pan = <&dsi_dual_nt35597_truly_video>; + pinctrl-names = "mdss_default", "mdss_sleep"; + pinctrl-0 = <&mdss_dsi_active &mdss_te_active>; + pinctrl-1 = <&mdss_dsi_suspend &mdss_te_suspend>; + qcom,platform-reset-gpio = <&tlmm 53 0>; + qcom,platform-te-gpio = <&tlmm 59 0>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi b/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi index 5a571c2db634..ba34f5ab378e 100644 --- a/arch/arm/boot/dts/qcom/sdm660-gpu.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-gpu.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 @@ -465,8 +465,8 @@ qcom,gpu-pwrlevel@2 { reg = <2>; qcom,gpu-freq = <465000000>; - qcom,bus-freq = <9>; - qcom,bus-min = <8>; + qcom,bus-freq = <10>; + qcom,bus-min = <9>; qcom,bus-max = <11>; }; @@ -474,18 +474,18 @@ qcom,gpu-pwrlevel@3 { reg = <3>; qcom,gpu-freq = <370000000>; - qcom,bus-freq = <8>; - qcom,bus-min = <6>; - qcom,bus-max = <9>; + qcom,bus-freq = <9>; + qcom,bus-min = <9>; + qcom,bus-max = <11>; }; /* Low SVS */ qcom,gpu-pwrlevel@4 { reg = <4>; qcom,gpu-freq = <266000000>; - qcom,bus-freq = <3>; + qcom,bus-freq = <4>; qcom,bus-min = <3>; - qcom,bus-max = <6>; + qcom,bus-max = <8>; }; /* Min SVS */ @@ -605,6 +605,69 @@ qcom,bus-max = <0>; }; }; + + qcom,gpu-pwrlevels-6 { + #address-cells = <1>; + #size-cells = <0>; + + qcom,speed-bin = <122>; + + qcom,initial-pwrlevel = <3>; + + /* NOM */ + qcom,gpu-pwrlevel@0 { + reg = <0>; + qcom,gpu-freq = <585000000>; + qcom,bus-freq = <12>; + qcom,bus-min = <11>; + qcom,bus-max = <12>; + }; + + /* SVS_L1 */ + qcom,gpu-pwrlevel@1 { + reg = <1>; + qcom,gpu-freq = <465000000>; + qcom,bus-freq = <9>; + qcom,bus-min = <8>; + qcom,bus-max = <11>; + }; + + /* SVS */ + qcom,gpu-pwrlevel@2 { + reg = <2>; + qcom,gpu-freq = <370000000>; + qcom,bus-freq = <8>; + qcom,bus-min = <6>; + qcom,bus-max = <9>; + }; + + /* Low SVS */ + qcom,gpu-pwrlevel@3 { + reg = <3>; + qcom,gpu-freq = <266000000>; + qcom,bus-freq = <3>; + qcom,bus-min = <3>; + qcom,bus-max = <6>; + }; + + /* Min SVS */ + qcom,gpu-pwrlevel@4 { + reg = <4>; + qcom,gpu-freq = <160000000>; + qcom,bus-freq = <3>; + qcom,bus-min = <3>; + qcom,bus-max = <5>; + }; + + /* XO */ + qcom,gpu-pwrlevel@5 { + reg = <5>; + qcom,gpu-freq = <19200000>; + qcom,bus-freq = <0>; + qcom,bus-min = <0>; + qcom,bus-max = <0>; + }; + }; }; }; diff --git a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi index a460773e5827..e6412bdc65cb 100644 --- a/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-mdss-panels.dtsi @@ -31,6 +31,7 @@ #include "dsi-panel-shenchao-td4310-1080p-video.dtsi" #include "dsi-panel-huaxing-nt36672-1080p-video.dtsi" #include "dsi-panel-lgd-incell-sw49106-fhd-video.dtsi" +#include "dsi-panel-hx8399c-fhd-plus-video.dtsi" &soc { dsi_panel_pwr_supply: dsi_panel_pwr_supply { @@ -395,3 +396,24 @@ qcom,mdss-dsi-t-clk-post = <0x0d>; qcom,mdss-dsi-t-clk-pre = <0x30>; }; + +&dsi_hx8399c_truly_vid { + qcom,mdss-dsi-panel-timings-phy-v2 = [24 1f 08 09 05 03 04 a0 + 24 1f 08 09 05 03 04 a0 + 24 1f 08 09 05 03 04 a0 + 24 1f 08 09 05 03 04 a0 + 24 1c 08 09 05 03 04 a0]; + 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_lp_mode"; + qcom,mdss-dsi-panel-status-value = <0x9d 0x9d 0x9d 0x9d>; + qcom,mdss-dsi-panel-on-check-value = <0x9d 0x9d 0x9d 0x9d>; + qcom,mdss-dsi-panel-status-read-length = <4>; + qcom,mdss-dsi-panel-max-error-count = <3>; + qcom,mdss-dsi-min-refresh-rate = <48>; + qcom,mdss-dsi-max-refresh-rate = <60>; + qcom,mdss-dsi-pan-enable-dynamic-fps; + qcom,mdss-dsi-pan-fps-update = + "dfps_immediate_porch_mode_vfp"; +}; diff --git a/arch/arm/boot/dts/qcom/sdm660-pm.dtsi b/arch/arm/boot/dts/qcom/sdm660-pm.dtsi index 679a1b89b2f8..9f1728117088 100644 --- a/arch/arm/boot/dts/qcom/sdm660-pm.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-pm.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 @@ -39,7 +39,7 @@ qcom,vctl-timeout-us = <500>; qcom,vctl-port = <0x0>; qcom,phase-port = <0x1>; - qcom,saw2-avs-ctl = <0x101c031>; + qcom,saw2-avs-ctl = <0x1010031>; qcom,saw2-avs-limit = <0x4580458>; qcom,pfm-port = <0x2>; }; diff --git a/arch/arm/boot/dts/qcom/sdm660-pm660a-fhd-cdp.dts b/arch/arm/boot/dts/qcom/sdm660-pm660a-fhd-cdp.dts new file mode 100644 index 000000000000..e8f5f29ae4cd --- /dev/null +++ b/arch/arm/boot/dts/qcom/sdm660-pm660a-fhd-cdp.dts @@ -0,0 +1,26 @@ +/* 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. + */ + + +/dts-v1/; + +#include "sdm660.dtsi" +#include "sdm660-cdp.dtsi" +#include "msm-pm660a.dtsi" +#include "sdm660-internal-codec.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. SDM 660 PM660 + PM660A Int. FHD Display"; + compatible = "qcom,sdm660-cdp", "qcom,sdm660", "qcom,cdp"; + qcom,board-id = <1 3>; + qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>; +}; diff --git a/arch/arm/boot/dts/qcom/sdm660-pm660a-qrd.dts b/arch/arm/boot/dts/qcom/sdm660-pm660a-qrd.dts index d9d74ea31d3d..7d8ece9c9895 100644 --- a/arch/arm/boot/dts/qcom/sdm660-pm660a-qrd.dts +++ b/arch/arm/boot/dts/qcom/sdm660-pm660a-qrd.dts @@ -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 @@ -33,6 +33,15 @@ qcom,mdss-pref-prim-intf = "dsi"; }; +&mdss_dp_ctrl { + pinctrl-names = "mdss_dp_active", "mdss_dp_sleep"; + pinctrl-0 = <&mdss_dp_aux_active &mdss_dp_usbplug_cc_active>; + pinctrl-1 = <&mdss_dp_aux_suspend &mdss_dp_usbplug_cc_suspend>; + qcom,aux-en-gpio = <&tlmm 55 0>; + qcom,aux-sel-gpio = <&tlmm 56 0>; + qcom,usbplug-cc-gpio = <&tlmm 58 0>; +}; + &mdss_dsi { hw-config = "single_dsi"; }; diff --git a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi b/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi index 8f06c3c4e909..735ec681777f 100644 --- a/arch/arm/boot/dts/qcom/sdm660-regulator.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-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 @@ -713,9 +713,9 @@ regulator-max-microvolt = <8>; qcom,cpr-fuse-corners = <5>; - qcom,cpr-fuse-combos = <32>; - qcom,cpr-speed-bins = <4>; - qcom,cpr-speed-bin-corners = <8 8 0 8>; + qcom,cpr-fuse-combos = <40>; + qcom,cpr-speed-bins = <5>; + qcom,cpr-speed-bin-corners = <8 8 0 8 8>; qcom,cpr-corners = /* Speed bin 0 */ <8 8 8 8 8 8 8 8>, @@ -727,7 +727,11 @@ <0 0 0 0 0 0 0 0>, /* Speed bin 3 */ + <8 8 8 8 8 8 8 8>, + + /* Speed bin 4 */ <8 8 8 8 8 8 8 8>; + qcom,cpr-corner-fmax-map = /* Speed bin 0 */ <2 3 4 5 8>, @@ -739,6 +743,9 @@ <0 0 0 0 0>, /* Speed bin 3 */ + <2 3 4 5 8>, + + /* Speed bin 4 */ <2 3 4 5 8>; qcom,cpr-voltage-ceiling = @@ -763,7 +770,12 @@ /* Speed bin 3 */ <300000000 633600000 902400000 1113600000 1401600000 1536000000 - 1612800000 1843200000>; + 1612800000 1843200000>, + + /* Speed bin 4 */ + <300000000 633600000 902400000 + 1113600000 1401600000 1536000000 + 1747200000 1843200000>; qcom,allow-voltage-interpolation; qcom,allow-quotient-interpolation; @@ -790,8 +802,8 @@ < (-4000) 4000 7000 19000 (-8000)>; qcom,cpr-closed-loop-voltage-fuse-adjustment = - <(-32000) (-30000) (-29000) (-38000) - (-36000)>; + <(-32000) (-30000) (-29000) (-23000) + (-21000)>; qcom,cpr-floor-to-ceiling-max-range = <32000 32000 32000 40000 44000 @@ -852,9 +864,9 @@ regulator-max-microvolt = <7>; qcom,cpr-fuse-corners = <5>; - qcom,cpr-fuse-combos = <32>; - qcom,cpr-speed-bins = <4>; - qcom,cpr-speed-bin-corners = <7 7 0 7>; + qcom,cpr-fuse-combos = <40>; + qcom,cpr-speed-bins = <5>; + qcom,cpr-speed-bin-corners = <7 7 0 7 7>; qcom,cpr-corners = /* Speed-bin 0 */ <7 7 7 7 7 7 7 7>, @@ -866,6 +878,9 @@ <0 0 0 0 0 0 0 0>, /* Speed-bin 3 */ + <7 7 7 7 7 7 7 7>, + + /* Speed-bin 4 */ <7 7 7 7 7 7 7 7>; qcom,cpr-corner-fmax-map = @@ -879,6 +894,9 @@ <0 0 0 0 0>, /* Speed-bin 3 */ + <2 3 4 6 7>, + + /* Speed-bin 4 */ <2 3 4 6 7>; qcom,cpr-voltage-ceiling = @@ -903,6 +921,11 @@ /* Speed bin 3 */ <300000000 1113600000 1401600000 1747200000 1804800000 2150400000 + 2208000000>, + + /* Speed bin 4 */ + <300000000 1113600000 1401600000 + 1747200000 1958400000 2150400000 2208000000>; qcom,allow-voltage-interpolation; diff --git a/arch/arm/boot/dts/qcom/sdm660.dtsi b/arch/arm/boot/dts/qcom/sdm660.dtsi index cc7e9fdd59b4..2c8fa9512719 100755 --- a/arch/arm/boot/dts/qcom/sdm660.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660.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 @@ -1263,18 +1263,10 @@ compatible = "qcom,clk-cpu-osm"; reg = <0x179c0000 0x4000>, <0x17916000 0x1000>, <0x17816000 0x1000>, <0x179d1000 0x1000>, - <0x00784130 0x8>, <0x00784130 0x8>, - <0x17914800 0x800>; + <0x00784130 0x8>, <0x00784130 0x8>; reg-names = "osm", "pwrcl_pll", "perfcl_pll", "apcs_common", "pwrcl_efuse", - "perfcl_efuse", "pwrcl_acd"; - - qcom,acdtd-val = <0x0000a111 0x0000a111>; - qcom,acdcr-val = <0x002c5ffd 0x002c5ffd>; - qcom,acdsscr-val = <0x00000901 0x00000901>; - qcom,acdextint0-val = <0x2cf9ae8 0x2cf9ae8>; - qcom,acdextint1-val = <0x2cf9afe 0x2cf9afe>; - qcom,acdautoxfer-val = <0x00000015 0x00000015>; + "perfcl_efuse"; vdd-pwrcl-supply = <&apc0_pwrcl_vreg>; vdd-perfcl-supply = <&apc1_perfcl_vreg>; @@ -1312,6 +1304,16 @@ < 1536000000 0x04040050 0x08400040 0x2 6 >, < 1612800000 0x04040054 0x09430043 0x2 7 >; + qcom,pwrcl-speedbin4-v0 = + < 300000000 0x0004000f 0x01200020 0x1 1 >, + < 633600000 0x05040021 0x03200020 0x1 2 >, + < 902400000 0x0404002f 0x04260026 0x1 3 >, + < 1113600000 0x0404003a 0x052e002e 0x2 4 >, + < 1401600000 0x04040049 0x073a003a 0x2 5 >, + < 1536000000 0x04040050 0x08400040 0x2 6 >, + < 1747200000 0x0404005b 0x09480048 0x2 7 >, + < 1843200000 0x04040060 0x094c004c 0x3 8 >; + qcom,perfcl-speedbin0-v0 = < 300000000 0x0004000f 0x01200020 0x1 1 >, < 1113600000 0x0404003a 0x052e002e 0x1 2 >, @@ -1337,9 +1339,15 @@ < 1747200000 0x0404005b 0x09480048 0x2 4 >, < 1804800000 0x0404005e 0x094b004b 0x2 5 >; + qcom,perfcl-speedbin4-v0 = + < 300000000 0x0004000f 0x01200020 0x1 1 >, + < 1113600000 0x0404003a 0x052e002e 0x1 2 >, + < 1401600000 0x04040049 0x073a003a 0x2 3 >, + < 1747200000 0x0404005b 0x09480048 0x2 4 >, + < 1958400000 0x04040066 0x0a510051 0x2 5 >; + qcom,up-timer = <1000 1000>; qcom,down-timer = <1000 1000>; - qcom,pc-override-index = <0 0>; qcom,set-ret-inactive; qcom,enable-llm-freq-vote; qcom,llm-freq-up-timer = <327675 327675>; @@ -1984,7 +1992,7 @@ vdd-1.8-xo-supply = <&pm660_l9_pin_ctrl>; vdd-1.3-rfa-supply = <&pm660_l6_pin_ctrl>; vdd-3.3-ch0-supply = <&pm660_l19_pin_ctrl>; - qcom,vdd-0.8-cx-mx-config = <525000 950000>; + qcom,vdd-0.8-cx-mx-config = <848000 848000>; qcom,vdd-1.8-xo-config = <1750000 1900000>; qcom,vdd-1.3-rfa-config = <1200000 1370000>; qcom,vdd-3.3-ch0-config = <3200000 3400000>; diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 0e4ecd355a6e..da660252e3fe 100755 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -101,6 +101,7 @@ config ARM64 select SYSCTL_EXCEPTION_TRACE select HAVE_CONTEXT_TRACKING select HAVE_ARM_SMCCC + select THREAD_INFO_IN_TASK help ARM 64-bit (AArch64) Linux support. @@ -785,6 +786,23 @@ config FORCE_MAX_ZONEORDER However for 4K, we choose a higher default value, 11 as opposed to 10, giving us 4M allocations matching the default size used by generic code. +config HARDEN_BRANCH_PREDICTOR + bool "Harden the branch predictor against aliasing attacks" if EXPERT + default y + help + Speculation attacks against some high-performance processors rely on + being able to manipulate the branch predictor for a victim context by + executing aliasing branches in the attacker context. Such attacks + can be partially mitigated against by clearing internal branch + predictor state and limiting the prediction logic in some situations. + + This config option will take CPU-specific actions to harden the + branch predictor against aliasing attacks and may rely on specific + instruction sequences or control bits being set by the system + firmware. + + If unsure, say Y. + menuconfig ARMV8_DEPRECATED bool "Emulate deprecated/obsolete ARMv8 instructions" depends on COMPAT diff --git a/arch/arm64/configs/msmcortex_mediabox-perf_defconfig b/arch/arm64/configs/msmcortex_mediabox-perf_defconfig index 79657f54ff7e..0a8d7476c760 100644 --- a/arch/arm64/configs/msmcortex_mediabox-perf_defconfig +++ b/arch/arm64/configs/msmcortex_mediabox-perf_defconfig @@ -343,6 +343,7 @@ CONFIG_APSS_CORE_EA=y CONFIG_MSM_APM=y CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y CONFIG_CPU_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y CONFIG_LIMITS_MONITOR=y CONFIG_LIMITS_LITE_HW=y CONFIG_THERMAL_MONITOR=y diff --git a/arch/arm64/configs/msmcortex_mediabox_defconfig b/arch/arm64/configs/msmcortex_mediabox_defconfig index 127bcc07b81b..af19abf351c0 100644 --- a/arch/arm64/configs/msmcortex_mediabox_defconfig +++ b/arch/arm64/configs/msmcortex_mediabox_defconfig @@ -345,6 +345,7 @@ CONFIG_APSS_CORE_EA=y CONFIG_MSM_APM=y CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y CONFIG_CPU_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y CONFIG_LIMITS_MONITOR=y CONFIG_LIMITS_LITE_HW=y CONFIG_THERMAL_MONITOR=y diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h index 02e4b7e4bdbf..0fbb6c64bac5 100644 --- a/arch/arm64/include/asm/assembler.h +++ b/arch/arm64/include/asm/assembler.h @@ -399,17 +399,4 @@ alternative_endif mrs \rd, sp_el0 .endm -/* - * Errata workaround post TTBR0_EL1 update. - */ - .macro post_ttbr0_update_workaround -#ifdef CONFIG_CAVIUM_ERRATUM_27456 -alternative_if ARM64_WORKAROUND_CAVIUM_27456 - ic iallu - dsb nsh - isb -alternative_else_nop_endif -#endif - .endm - #endif /* __ASM_ASSEMBLER_H */ diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index 29f8d7fd627d..efc354e04d91 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -36,7 +36,8 @@ #define ARM64_WORKAROUND_CAVIUM_27456 11 #define ARM64_HAS_VIRT_HOST_EXTN 12 -#define ARM64_NCAPS 13 +#define ARM64_HARDEN_BRANCH_PREDICTOR 13 +#define ARM64_NCAPS 14 #ifndef __ASSEMBLY__ @@ -169,7 +170,9 @@ void __init setup_cpu_features(void); void update_cpu_capabilities(const struct arm64_cpu_capabilities *caps, const char *info); +void enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps); void check_local_cpu_errata(void); +void __init enable_errata_workarounds(void); #ifdef CONFIG_HOTPLUG_CPU void verify_local_cpu_capabilities(void); diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h index 1ddc9c930097..78e0aebbc1f2 100644 --- a/arch/arm64/include/asm/cputype.h +++ b/arch/arm64/include/asm/cputype.h @@ -79,10 +79,14 @@ #define ARM_CPU_PART_AEM_V8 0xD0F #define ARM_CPU_PART_FOUNDATION 0xD00 #define ARM_CPU_PART_CORTEX_A57 0xD07 +#define ARM_CPU_PART_CORTEX_A72 0xD08 #define ARM_CPU_PART_CORTEX_A53 0xD03 #define ARM_CPU_PART_CORTEX_A72 0xD08 +#define ARM_CPU_PART_CORTEX_A73 0xD09 +#define ARM_CPU_PART_CORTEX_A75 0xD0A #define ARM_CPU_PART_KRYO2XX_GOLD 0x800 #define ARM_CPU_PART_KRYO2XX_SILVER 0x801 +#define QCOM_CPU_PART_KRYO 0x200 #define APM_CPU_PART_POTENZA 0x000 @@ -90,9 +94,15 @@ #define MIDR_CORTEX_A53 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53) #define MIDR_CORTEX_A57 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A57) +#define MIDR_CORTEX_A72 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A72) +#define MIDR_CORTEX_A73 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A73) +#define MIDR_CORTEX_A75 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A75) #define MIDR_THUNDERX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX) #define MIDR_KRYO2XX_SILVER \ MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, ARM_CPU_PART_KRYO2XX_SILVER) +#define MIDR_KRYO2XX_GOLD \ + MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, ARM_CPU_PART_KRYO2XX_GOLD) +#define MIDR_QCOM_KRYO MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_KRYO) #ifndef __ASSEMBLY__ diff --git a/arch/arm64/include/asm/current.h b/arch/arm64/include/asm/current.h new file mode 100644 index 000000000000..2e61d21294ba --- /dev/null +++ b/arch/arm64/include/asm/current.h @@ -0,0 +1,27 @@ +#ifndef __ASM_CURRENT_H +#define __ASM_CURRENT_H + +#include + +#include + +#ifndef __ASSEMBLY__ + +#ifdef CONFIG_THREAD_INFO_IN_TASK +struct task_struct; + +static __always_inline struct task_struct *get_current(void) +{ + return (struct task_struct *)read_sysreg(sp_el0); +} +#define current get_current() +#else +#include +#define get_current() (current_thread_info()->task) +#define current get_current() +#endif + +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_CURRENT_H */ + diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index 990124a67eeb..613268ade961 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -16,6 +16,11 @@ #ifndef __ASM_MMU_H #define __ASM_MMU_H +#include + +#include +#include + typedef struct { atomic64_t id; void *vdso; @@ -28,6 +33,43 @@ typedef struct { */ #define ASID(mm) ((mm)->context.id.counter & 0xffff) +typedef void (*bp_hardening_cb_t)(void); + +struct bp_hardening_data { + int hyp_vectors_slot; + bp_hardening_cb_t fn; +}; + +#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR +extern char __bp_harden_hyp_vecs_start[], __bp_harden_hyp_vecs_end[]; + +DECLARE_PER_CPU_READ_MOSTLY(struct bp_hardening_data, bp_hardening_data); + +static inline struct bp_hardening_data *arm64_get_bp_hardening_data(void) +{ + return this_cpu_ptr(&bp_hardening_data); +} + +static inline void arm64_apply_bp_hardening(void) +{ + struct bp_hardening_data *d; + + if (!cpus_have_cap(ARM64_HARDEN_BRANCH_PREDICTOR)) + return; + + d = arm64_get_bp_hardening_data(); + if (d->fn) + d->fn(); +} +#else +static inline struct bp_hardening_data *arm64_get_bp_hardening_data(void) +{ + return NULL; +} + +static inline void arm64_apply_bp_hardening(void) { } +#endif /* CONFIG_HARDEN_BRANCH_PREDICTOR */ + extern void paging_init(void); extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt); extern void init_mem_pgprot(void); diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h index 2013a4dc5124..9c0e5dc43246 100644 --- a/arch/arm64/include/asm/smp.h +++ b/arch/arm64/include/asm/smp.h @@ -57,6 +57,9 @@ asmlinkage void secondary_start_kernel(void); */ struct secondary_data { void *stack; +#ifdef CONFIG_THREAD_INFO_IN_TASK + struct task_struct *task; +#endif }; extern struct secondary_data secondary_data; extern void secondary_entry(void); diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 0961a24e8d48..90025558d5e1 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -112,6 +112,7 @@ #define ID_AA64ISAR0_AES_SHIFT 4 /* id_aa64pfr0 */ +#define ID_AA64PFR0_CSV2_SHIFT 56 #define ID_AA64PFR0_GIC_SHIFT 24 #define ID_AA64PFR0_ASIMD_SHIFT 20 #define ID_AA64PFR0_FP_SHIFT 16 diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h index 4bb038ec6453..f799cc08e2cc 100644 --- a/arch/arm64/include/asm/thread_info.h +++ b/arch/arm64/include/asm/thread_info.h @@ -47,14 +47,25 @@ typedef unsigned long mm_segment_t; struct thread_info { unsigned long flags; /* low level flags */ mm_segment_t addr_limit; /* address limit */ +#ifndef CONFIG_THREAD_INFO_IN_TASK struct task_struct *task; /* main task structure */ +#endif #ifdef CONFIG_ARM64_SW_TTBR0_PAN u64 ttbr0; /* saved TTBR0_EL1 */ #endif int preempt_count; /* 0 => preemptable, <0 => bug */ +#ifndef CONFIG_THREAD_INFO_IN_TASK int cpu; /* cpu */ +#endif }; +#ifdef CONFIG_THREAD_INFO_IN_TASK +#define INIT_THREAD_INFO(tsk) \ +{ \ + .preempt_count = INIT_PREEMPT_COUNT, \ + .addr_limit = KERNEL_DS, \ +} +#else #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ @@ -64,7 +75,6 @@ struct thread_info { } #define init_thread_info (init_thread_union.thread_info) -#define init_stack (init_thread_union.stack) /* * how to get the current stack pointer from C @@ -87,6 +97,9 @@ static inline struct thread_info *current_thread_info(void) return (struct thread_info *)sp_el0; } +#endif + +#define init_stack (init_thread_union.stack) #define thread_saved_pc(tsk) \ ((unsigned long)(tsk->thread.cpu_context.pc)) diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index bc1b1b0ed6ff..0fe8d9cc1617 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -49,6 +49,10 @@ arm64-obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate-asm.o arm64-obj-$(CONFIG_ARM64_ACPI_PARKING_PROTOCOL) += acpi_parking_protocol.o arm64-obj-$(CONFIG_PARAVIRT) += paravirt.o +ifeq ($(CONFIG_KVM),y) +arm64-obj-$(CONFIG_HARDEN_BRANCH_PREDICTOR) += bpi.o +endif + obj-y += $(arm64-obj-y) vdso/ probes/ obj-m += $(arm64-obj-m) head-y := head.o diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index 350c0e99fc6b..36c4307c4af3 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -35,11 +35,16 @@ int main(void) { DEFINE(TSK_ACTIVE_MM, offsetof(struct task_struct, active_mm)); BLANK(); +#ifdef CONFIG_THREAD_INFO_IN_TASK + DEFINE(TSK_TI_FLAGS, offsetof(struct task_struct, thread_info.flags)); + DEFINE(TSK_TI_PREEMPT, offsetof(struct task_struct, thread_info.preempt_count)); + DEFINE(TSK_TI_ADDR_LIMIT, offsetof(struct task_struct, thread_info.addr_limit)); + DEFINE(TSK_STACK, offsetof(struct task_struct, stack)); +#else DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count)); DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit)); - DEFINE(TI_TASK, offsetof(struct thread_info, task)); - DEFINE(TI_CPU, offsetof(struct thread_info, cpu)); +#endif #ifdef CONFIG_ARM64_SW_TTBR0_PAN DEFINE(TSK_TI_TTBR0, offsetof(struct thread_info, ttbr0)); #endif @@ -124,6 +129,11 @@ int main(void) DEFINE(TZ_MINWEST, offsetof(struct timezone, tz_minuteswest)); DEFINE(TZ_DSTTIME, offsetof(struct timezone, tz_dsttime)); BLANK(); +#ifdef CONFIG_THREAD_INFO_IN_TASK + DEFINE(CPU_BOOT_STACK, offsetof(struct secondary_data, stack)); + DEFINE(CPU_BOOT_TASK, offsetof(struct secondary_data, task)); + BLANK(); +#endif #ifdef CONFIG_KVM_ARM_HOST DEFINE(VCPU_CONTEXT, offsetof(struct kvm_vcpu, arch.ctxt)); DEFINE(CPU_GP_REGS, offsetof(struct kvm_cpu_context, gp_regs)); diff --git a/arch/arm64/kernel/bpi.S b/arch/arm64/kernel/bpi.S new file mode 100644 index 000000000000..76225c2611ea --- /dev/null +++ b/arch/arm64/kernel/bpi.S @@ -0,0 +1,87 @@ +/* + * Contains CPU specific branch predictor invalidation sequences + * + * Copyright (C) 2018 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +.macro ventry target + .rept 31 + nop + .endr + b \target +.endm + +.macro vectors target + ventry \target + 0x000 + ventry \target + 0x080 + ventry \target + 0x100 + ventry \target + 0x180 + + ventry \target + 0x200 + ventry \target + 0x280 + ventry \target + 0x300 + ventry \target + 0x380 + + ventry \target + 0x400 + ventry \target + 0x480 + ventry \target + 0x500 + ventry \target + 0x580 + + ventry \target + 0x600 + ventry \target + 0x680 + ventry \target + 0x700 + ventry \target + 0x780 +.endm + + .align 11 +ENTRY(__bp_harden_hyp_vecs_start) + .rept 4 + vectors __kvm_hyp_vector + .endr +ENTRY(__bp_harden_hyp_vecs_end) +ENTRY(__psci_hyp_bp_inval_start) + sub sp, sp, #(8 * 18) + stp x16, x17, [sp, #(16 * 0)] + stp x14, x15, [sp, #(16 * 1)] + stp x12, x13, [sp, #(16 * 2)] + stp x10, x11, [sp, #(16 * 3)] + stp x8, x9, [sp, #(16 * 4)] + stp x6, x7, [sp, #(16 * 5)] + stp x4, x5, [sp, #(16 * 6)] + stp x2, x3, [sp, #(16 * 7)] + stp x0, x1, [sp, #(16 * 8)] + mov x0, #0x84000000 + smc #0 + ldp x16, x17, [sp, #(16 * 0)] + ldp x14, x15, [sp, #(16 * 1)] + ldp x12, x13, [sp, #(16 * 2)] + ldp x10, x11, [sp, #(16 * 3)] + ldp x8, x9, [sp, #(16 * 4)] + ldp x6, x7, [sp, #(16 * 5)] + ldp x4, x5, [sp, #(16 * 6)] + ldp x2, x3, [sp, #(16 * 7)] + ldp x0, x1, [sp, #(16 * 8)] + add sp, sp, #(8 * 18) +ENTRY(__psci_hyp_bp_inval_end) + +ENTRY(__qcom_hyp_sanitize_link_stack_start) + stp x29, x30, [sp, #-16]! + .rept 16 + bl . + 4 + .endr + ldp x29, x30, [sp], #16 +ENTRY(__qcom_hyp_sanitize_link_stack_end) diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c index b3f2d13d8ff1..e857248dd980 100644 --- a/arch/arm64/kernel/cpu_errata.c +++ b/arch/arm64/kernel/cpu_errata.c @@ -29,12 +29,160 @@ is_affected_midr_range(const struct arm64_cpu_capabilities *entry) entry->midr_range_max); } +static bool __maybe_unused +is_kryo_midr(const struct arm64_cpu_capabilities *entry) +{ + u32 model; + + model = read_cpuid_id(); + model &= MIDR_IMPLEMENTOR_MASK | (0xf00 << MIDR_PARTNUM_SHIFT) | + MIDR_ARCHITECTURE_MASK; + + return model == entry->midr_model; +} + +#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR +#include +#include + +DEFINE_PER_CPU_READ_MOSTLY(struct bp_hardening_data, bp_hardening_data); + +#ifdef CONFIG_KVM +extern char __psci_hyp_bp_inval_start[], __psci_hyp_bp_inval_end[]; +extern char __qcom_hyp_sanitize_link_stack_start[]; +extern char __qcom_hyp_sanitize_link_stack_end[]; + +static void __copy_hyp_vect_bpi(int slot, const char *hyp_vecs_start, + const char *hyp_vecs_end) +{ + void *dst = lm_alias(__bp_harden_hyp_vecs_start + slot * SZ_2K); + int i; + + for (i = 0; i < SZ_2K; i += 0x80) + memcpy(dst + i, hyp_vecs_start, hyp_vecs_end - hyp_vecs_start); + + flush_icache_range((uintptr_t)dst, (uintptr_t)dst + SZ_2K); +} + +static void __install_bp_hardening_cb(bp_hardening_cb_t fn, + const char *hyp_vecs_start, + const char *hyp_vecs_end) +{ + static int last_slot = -1; + static DEFINE_SPINLOCK(bp_lock); + int cpu, slot = -1; + + spin_lock(&bp_lock); + for_each_possible_cpu(cpu) { + if (per_cpu(bp_hardening_data.fn, cpu) == fn) { + slot = per_cpu(bp_hardening_data.hyp_vectors_slot, cpu); + break; + } + } + + if (slot == -1) { + last_slot++; + BUG_ON(((__bp_harden_hyp_vecs_end - __bp_harden_hyp_vecs_start) + / SZ_2K) <= last_slot); + slot = last_slot; + __copy_hyp_vect_bpi(slot, hyp_vecs_start, hyp_vecs_end); + } + + __this_cpu_write(bp_hardening_data.hyp_vectors_slot, slot); + __this_cpu_write(bp_hardening_data.fn, fn); + spin_unlock(&bp_lock); +} +#else +#define __psci_hyp_bp_inval_start NULL +#define __psci_hyp_bp_inval_end NULL +#define __qcom_hyp_sanitize_link_stack_start NULL +#define __qcom_hyp_sanitize_link_stack_end NULL + +static void __maybe_unused __install_bp_hardening_cb(bp_hardening_cb_t fn, + const char *hyp_vecs_start, + const char *hyp_vecs_end) +{ + __this_cpu_write(bp_hardening_data.fn, fn); +} +#endif /* CONFIG_KVM */ + +static void __maybe_unused install_bp_hardening_cb( + const struct arm64_cpu_capabilities *entry, + bp_hardening_cb_t fn, + const char *hyp_vecs_start, + const char *hyp_vecs_end) +{ + u64 pfr0; + + if (!entry->matches(entry)) + return; + + pfr0 = read_cpuid(SYS_ID_AA64PFR0_EL1); + if (cpuid_feature_extract_unsigned_field(pfr0, ID_AA64PFR0_CSV2_SHIFT)) + return; + + __install_bp_hardening_cb(fn, hyp_vecs_start, hyp_vecs_end); +} + +#include + +static int enable_psci_bp_hardening(void *data) +{ + const struct arm64_cpu_capabilities *entry = data; + + if (psci_ops.get_version) + install_bp_hardening_cb(entry, + (bp_hardening_cb_t)psci_ops.get_version, + __psci_hyp_bp_inval_start, + __psci_hyp_bp_inval_end); + + return 0; +} + +static void __maybe_unused qcom_link_stack_sanitization(void) +{ + u64 tmp; + + asm volatile("mov %0, x30 \n" + ".rept 16 \n" + "bl . + 4 \n" + ".endr \n" + "mov x30, %0 \n" + : "=&r" (tmp)); +} + +static void __maybe_unused qcom_bp_hardening(void) +{ + qcom_link_stack_sanitization(); + if (psci_ops.get_version) + psci_ops.get_version(); +} + +static int __maybe_unused enable_qcom_bp_hardening(void *data) +{ + const struct arm64_cpu_capabilities *entry = data; + + install_bp_hardening_cb(entry, + (bp_hardening_cb_t)qcom_bp_hardening, + __psci_hyp_bp_inval_start, + __psci_hyp_bp_inval_end); + return 0; +} + +#endif /* CONFIG_HARDEN_BRANCH_PREDICTOR */ + #define MIDR_RANGE(model, min, max) \ .matches = is_affected_midr_range, \ .midr_model = model, \ .midr_range_min = min, \ .midr_range_max = max +#define MIDR_ALL_VERSIONS(model) \ + .matches = is_affected_midr_range, \ + .midr_model = model, \ + .midr_range_min = 0, \ + .midr_range_max = (MIDR_VARIANT_MASK | MIDR_REVISION_MASK) + const struct arm64_cpu_capabilities arm64_errata[] = { #if defined(CONFIG_ARM64_ERRATUM_826319) || \ defined(CONFIG_ARM64_ERRATUM_827319) || \ @@ -102,6 +250,39 @@ const struct arm64_cpu_capabilities arm64_errata[] = { MIDR_RANGE(MIDR_THUNDERX, 0x00, (1 << MIDR_VARIANT_SHIFT) | 1), }, +#endif +#ifdef CONFIG_HARDEN_BRANCH_PREDICTOR + { + .capability = ARM64_HARDEN_BRANCH_PREDICTOR, + MIDR_ALL_VERSIONS(MIDR_CORTEX_A57), + .enable = enable_psci_bp_hardening, + }, + { + .capability = ARM64_HARDEN_BRANCH_PREDICTOR, + MIDR_ALL_VERSIONS(MIDR_CORTEX_A72), + .enable = enable_psci_bp_hardening, + }, + { + .capability = ARM64_HARDEN_BRANCH_PREDICTOR, + MIDR_ALL_VERSIONS(MIDR_CORTEX_A73), + .enable = enable_psci_bp_hardening, + }, + { + .capability = ARM64_HARDEN_BRANCH_PREDICTOR, + MIDR_ALL_VERSIONS(MIDR_CORTEX_A75), + .enable = enable_psci_bp_hardening, + }, + { + .capability = ARM64_HARDEN_BRANCH_PREDICTOR, + MIDR_ALL_VERSIONS(MIDR_KRYO2XX_GOLD), + .enable = enable_psci_bp_hardening, + }, + { + .capability = ARM64_HARDEN_BRANCH_PREDICTOR, + .midr_model = MIDR_QCOM_KRYO, + .matches = is_kryo_midr, + .enable = enable_qcom_bp_hardening, + }, #endif { } @@ -111,3 +292,8 @@ void check_local_cpu_errata(void) { update_cpu_capabilities(arm64_errata, "enabling workaround for"); } + +void __init enable_errata_workarounds(void) +{ + enable_cpu_capabilities(arm64_errata); +} diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index f75000996e4c..3bd58b5c0a81 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -91,6 +91,7 @@ static struct arm64_ftr_bits ftr_id_aa64isar0[] = { static struct arm64_ftr_bits ftr_id_aa64pfr0[] = { ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 32, 32, 0), ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 28, 4, 0), + ARM64_FTR_BITS(FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64PFR0_CSV2_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64PFR0_GIC_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_ASIMD_SHIFT, 4, ID_AA64PFR0_ASIMD_NI), ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_FP_SHIFT, 4, ID_AA64PFR0_FP_NI), @@ -819,8 +820,7 @@ void update_cpu_capabilities(const struct arm64_cpu_capabilities *caps, * Run through the enabled capabilities and enable() it on all active * CPUs */ -static void __init -enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps) +void __init enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps) { int i; @@ -832,7 +832,8 @@ enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps) * uses an IPI, giving us a PSTATE that disappears when * we return. */ - stop_machine(caps[i].enable, NULL, cpu_online_mask); + stop_machine(caps[i].enable, (void *)&caps[i], + cpu_online_mask); } #ifdef CONFIG_HOTPLUG_CPU @@ -947,7 +948,7 @@ void verify_local_cpu_capabilities(void) if (!feature_matches(__raw_read_system_reg(caps[i].sys_reg), &caps[i])) fail_incapable_cpu("arm64_features", &caps[i]); if (caps[i].enable) - caps[i].enable(NULL); + caps[i].enable((void *)&caps[i]); } for (i = 0, caps = arm64_hwcaps; caps[i].matches; i++) { @@ -979,6 +980,7 @@ void __init setup_cpu_features(void) /* Set the CPU feature capabilies */ setup_feature_capabilities(); + enable_errata_workarounds(); setup_cpu_hwcaps(); /* Advertise that we have computed the system capabilities */ diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 758a7eb13381..83ca137e018b 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -93,9 +93,14 @@ .if \el == 0 mrs x21, sp_el0 +#ifdef CONFIG_THREAD_INFO_IN_TASK + ldr_this_cpu tsk, __entry_task, x20 // Ensure MDSCR_EL1.SS is clear, + ldr x19, [tsk, #TSK_TI_FLAGS] // since we can unmask debug +#else mov tsk, sp and tsk, tsk, #~(THREAD_SIZE - 1) // Ensure MDSCR_EL1.SS is clear, ldr x19, [tsk, #TI_FLAGS] // since we can unmask debug +#endif disable_step_tsk x19, x20 // exceptions when scheduling. mov x29, xzr // fp pointed to user-space @@ -103,10 +108,18 @@ add x21, sp, #S_FRAME_SIZE get_thread_info tsk /* Save the task's original addr_limit and set USER_DS (TASK_SIZE_64) */ +#ifdef CONFIG_THREAD_INFO_IN_TASK + ldr x20, [tsk, #TSK_TI_ADDR_LIMIT] +#else ldr x20, [tsk, #TI_ADDR_LIMIT] +#endif str x20, [sp, #S_ORIG_ADDR_LIMIT] mov x20, #TASK_SIZE_64 +#ifdef CONFIG_THREAD_INFO_IN_TASK + str x20, [tsk, #TSK_TI_ADDR_LIMIT] +#else str x20, [tsk, #TI_ADDR_LIMIT] +#endif ALTERNATIVE(nop, SET_PSTATE_UAO(0), ARM64_HAS_UAO, CONFIG_ARM64_UAO) .endif /* \el == 0 */ mrs x22, elr_el1 @@ -168,7 +181,11 @@ alternative_else_nop_endif .if \el != 0 /* Restore the task's original addr_limit. */ ldr x20, [sp, #S_ORIG_ADDR_LIMIT] +#ifdef CONFIG_THREAD_INFO_IN_TASK + str x20, [tsk, #TSK_TI_ADDR_LIMIT] +#else str x20, [tsk, #TI_ADDR_LIMIT] +#endif /* No need to restore UAO, it will be restored from SPSR_EL1 */ .endif @@ -200,7 +217,7 @@ alternative_else_nop_endif * Cavium erratum 27456 (broadcast TLBI instructions may cause I-cache * corruption). */ - post_ttbr0_update_workaround + bl post_ttbr_update_workaround .endif 1: .if \el != 0 @@ -258,13 +275,20 @@ alternative_endif mov x19, sp // preserve the original sp /* - * Compare sp with the current thread_info, if the top - * ~(THREAD_SIZE - 1) bits match, we are on a task stack, and - * should switch to the irq stack. + * Compare sp with the base of the task stack. + * If the top ~(THREAD_SIZE - 1) bits match, we are on a task stack, + * and should switch to the irq stack. */ +#ifdef CONFIG_THREAD_INFO_IN_TASK + ldr x25, [tsk, TSK_STACK] + eor x25, x25, x19 + and x25, x25, #~(THREAD_SIZE - 1) + cbnz x25, 9998f +#else and x25, x19, #~(THREAD_SIZE - 1) cmp x25, tsk b.ne 9998f +#endif this_cpu_ptr irq_stack, x25, x26 mov x26, #IRQ_STACK_START_SP @@ -498,9 +522,17 @@ el1_irq: irq_handler #ifdef CONFIG_PREEMPT +#ifdef CONFIG_THREAD_INFO_IN_TASK + ldr w24, [tsk, #TSK_TI_PREEMPT] // get preempt count +#else ldr w24, [tsk, #TI_PREEMPT] // get preempt count +#endif cbnz w24, 1f // preempt count != 0 +#ifdef CONFIG_THREAD_INFO_IN_TASK + ldr x0, [tsk, #TSK_TI_FLAGS] // get flags +#else ldr x0, [tsk, #TI_FLAGS] // get flags +#endif tbz x0, #TIF_NEED_RESCHED, 1f // needs rescheduling? bl el1_preempt 1: @@ -515,7 +547,11 @@ ENDPROC(el1_irq) el1_preempt: mov x24, lr 1: bl preempt_schedule_irq // irq en/disable is done inside +#ifdef CONFIG_THREAD_INFO_IN_TASK + ldr x0, [tsk, #TSK_TI_FLAGS] // get new tasks TI_FLAGS +#else ldr x0, [tsk, #TI_FLAGS] // get new tasks TI_FLAGS +#endif tbnz x0, #TIF_NEED_RESCHED, 1b // needs rescheduling? ret x24 #endif @@ -616,13 +652,15 @@ el0_ia: * Instruction abort handling */ mrs x26, far_el1 - // enable interrupts before calling the main handler - enable_dbg_and_irq + enable_dbg +#ifdef CONFIG_TRACE_IRQFLAGS + bl trace_hardirqs_off +#endif ct_user_exit mov x0, x26 mov x1, x25 mov x2, sp - bl do_mem_abort + bl do_el0_ia_bp_hardening b ret_to_user el0_fpsimd_acc: /* @@ -783,8 +821,12 @@ ENTRY(cpu_switch_to) mov v15.16b, v15.16b #endif mov sp, x9 +#ifdef CONFIG_THREAD_INFO_IN_TASK + msr sp_el0, x1 +#else and x9, x9, #~(THREAD_SIZE - 1) msr sp_el0, x9 +#endif ret ENDPROC(cpu_switch_to) @@ -795,7 +837,11 @@ ENDPROC(cpu_switch_to) ret_fast_syscall: disable_irq // disable interrupts str x0, [sp, #S_X0] // returned x0 +#ifdef CONFIG_THREAD_INFO_IN_TASK + ldr x1, [tsk, #TSK_TI_FLAGS] // re-check for syscall tracing +#else ldr x1, [tsk, #TI_FLAGS] // re-check for syscall tracing +#endif and x2, x1, #_TIF_SYSCALL_WORK cbnz x2, ret_fast_syscall_trace and x2, x1, #_TIF_WORK_MASK @@ -827,7 +873,11 @@ work_resched: */ ret_to_user: disable_irq // disable interrupts +#ifdef CONFIG_THREAD_INFO_IN_TASK + ldr x1, [tsk, #TSK_TI_FLAGS] +#else ldr x1, [tsk, #TI_FLAGS] +#endif and x2, x1, #_TIF_WORK_MASK cbnz x2, work_pending enable_step_tsk x1, x2 @@ -859,7 +909,11 @@ el0_svc_naked: // compat entry point enable_dbg_and_irq ct_user_exit 1 +#ifdef CONFIG_THREAD_INFO_IN_TASK + ldr x16, [tsk, #TSK_TI_FLAGS] // check for syscall hooks +#else ldr x16, [tsk, #TI_FLAGS] // check for syscall hooks +#endif tst x16, #_TIF_SYSCALL_WORK b.ne __sys_trace cmp scno, sc_nr // check upper syscall limit diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S index a1c2ac38771d..0a0cd0476665 100644 --- a/arch/arm64/kernel/head.S +++ b/arch/arm64/kernel/head.S @@ -418,6 +418,7 @@ ENDPROC(__create_page_tables) .set initial_sp, init_thread_union + THREAD_START_SP __primary_switched: mov x28, lr // preserve LR + adr_l x8, vectors // load VBAR_EL1 with virtual msr vbar_el1, x8 // vector table address isb @@ -430,10 +431,18 @@ __primary_switched: bl __pi_memset dsb ishst // Make zero page visible to PTW +#ifdef CONFIG_THREAD_INFO_IN_TASK + adrp x4, init_thread_union + add sp, x4, #THREAD_SIZE + adr_l x5, init_task + msr sp_el0, x5 // Save thread_info +#else adr_l sp, initial_sp, x4 mov x4, sp and x4, x4, #~(THREAD_SIZE - 1) msr sp_el0, x4 // Save thread_info +#endif + str_l x21, __fdt_pointer, x5 // Save FDT pointer ldr_l x4, kimage_vaddr // Save the offset between @@ -642,11 +651,18 @@ __secondary_switched: adr_l x5, vectors msr vbar_el1, x5 isb - +#ifdef CONFIG_THREAD_INFO_IN_TASK + adr_l x0, secondary_data + ldr x1, [x0, #CPU_BOOT_STACK] // get secondary_data.stack + mov sp, x1 + ldr x2, [x0, #CPU_BOOT_TASK] + msr sp_el0, x2 +#else ldr_l x0, secondary_data // get secondary_data.stack mov sp, x0 and x0, x0, #~(THREAD_SIZE - 1) msr sp_el0, x0 // save thread_info +#endif mov x29, #0 b secondary_start_kernel ENDPROC(__secondary_switched) diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index fc0a7aa2ca82..8997317686f1 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c @@ -45,6 +45,9 @@ #include #include #include +#ifdef CONFIG_THREAD_INFO_IN_TASK +#include +#endif #include #include @@ -394,6 +397,22 @@ void uao_thread_switch(struct task_struct *next) } } +#ifdef CONFIG_THREAD_INFO_IN_TASK +/* + * We store our current task in sp_el0, which is clobbered by userspace. Keep a + * shadow copy so that we can restore this upon entry from userspace. + * + * This is *only* for exception entry from EL0, and is not valid until we + * __switch_to() a user task. + */ +DEFINE_PER_CPU(struct task_struct *, __entry_task); + +static void entry_task_switch(struct task_struct *next) +{ + __this_cpu_write(__entry_task, next); +} +#endif + /* * Thread switching. */ @@ -406,6 +425,9 @@ struct task_struct *__switch_to(struct task_struct *prev, tls_thread_switch(next); hw_breakpoint_thread_switch(next); contextidr_thread_switch(next); +#ifdef CONFIG_THREAD_INFO_IN_TASK + entry_task_switch(next); +#endif uao_thread_switch(next); /* diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index d031a85e0bc7..31f867f7836c 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -98,6 +98,9 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle) * We need to tell the secondary core where to find its stack and the * page tables. */ +#ifdef CONFIG_THREAD_INFO_IN_TASK + secondary_data.task = idle; +#endif secondary_data.stack = task_stack_page(idle) + THREAD_START_SP; __flush_dcache_area(&secondary_data, sizeof(secondary_data)); @@ -121,6 +124,9 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle) pr_err("CPU%u: failed to boot: %d\n", cpu, ret); } +#ifdef CONFIG_THREAD_INFO_IN_TASK + secondary_data.task = NULL; +#endif secondary_data.stack = NULL; return ret; diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c index 25128089c386..b572604fe405 100644 --- a/arch/arm64/mm/context.c +++ b/arch/arm64/mm/context.c @@ -182,6 +182,9 @@ void check_and_switch_context(struct mm_struct *mm, unsigned int cpu) raw_spin_unlock_irqrestore(&cpu_asid_lock, flags); switch_mm_fastpath: + + arm64_apply_bp_hardening(); + /* * Defer TTBR0_EL1 setting for user threads to uaccess_enable() when * emulating PAN. @@ -190,6 +193,15 @@ void check_and_switch_context(struct mm_struct *mm, unsigned int cpu) cpu_switch_mm(mm->pgd, mm); } +/* Errata workaround post TTBRx_EL1 update. */ +asmlinkage void post_ttbr_update_workaround(void) +{ + asm(ALTERNATIVE("nop; nop; nop", + "ic iallu; dsb nsh; isb", + ARM64_WORKAROUND_CAVIUM_27456, + CONFIG_CAVIUM_ERRATUM_27456)); +} + static int asids_init(void) { int fld = cpuid_feature_extract_field(read_cpuid(SYS_ID_AA64MMFR0_EL1), 4); diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 1804aea44faa..c68202ce2536 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -606,6 +606,22 @@ asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr, arm64_notify_die("", regs, &info, esr); } +asmlinkage void __exception do_el0_ia_bp_hardening(unsigned long addr, + unsigned int esr, + struct pt_regs *regs) +{ + /* + * We've taken an instruction abort from userspace and not yet + * re-enabled IRQs. If the address is a kernel address, apply + * BP hardening prior to enabling IRQs and pre-emption. + */ + if (addr > TASK_SIZE) + arm64_apply_bp_hardening(); + + local_irq_enable(); + do_mem_abort(addr, esr, regs); +} + /* * Handle stack alignment exceptions. */ diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S index 81a0de4e457d..ce822bbe375b 100644 --- a/arch/arm64/mm/proc.S +++ b/arch/arm64/mm/proc.S @@ -171,8 +171,7 @@ ENTRY(cpu_do_switch_mm) bfi x0, x1, #48, #16 // set the ASID msr ttbr0_el1, x0 // set TTBR0 isb - post_ttbr0_update_workaround - ret + b post_ttbr_update_workaround // Back to C code... ENDPROC(cpu_do_switch_mm) .pushsection ".idmap.text", "ax" diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index 37ade035c956..9075f08b392a 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -1078,10 +1078,8 @@ int blkcg_init_queue(struct request_queue *q) if (preloaded) radix_tree_preload_end(); - if (IS_ERR(blkg)) { - blkg_free(new_blkg); + if (IS_ERR(blkg)) return PTR_ERR(blkg); - } q->root_blkg = blkg; q->root_rl.blkg = blkg; diff --git a/crypto/hmac.c b/crypto/hmac.c index 72e38c098bb3..ba07fb6221ae 100644 --- a/crypto/hmac.c +++ b/crypto/hmac.c @@ -194,11 +194,15 @@ static int hmac_create(struct crypto_template *tmpl, struct rtattr **tb) salg = shash_attr_alg(tb[1], 0, 0); if (IS_ERR(salg)) return PTR_ERR(salg); + alg = &salg->base; + /* The underlying hash algorithm must be unkeyed */ err = -EINVAL; + if (crypto_shash_alg_has_setkey(salg)) + goto out_put_alg; + ds = salg->digestsize; ss = salg->statesize; - alg = &salg->base; if (ds > alg->cra_blocksize || ss < alg->cra_blocksize) goto out_put_alg; diff --git a/crypto/pcrypt.c b/crypto/pcrypt.c index ee9cfb99fe25..f8ec3d4ba4a8 100644 --- a/crypto/pcrypt.c +++ b/crypto/pcrypt.c @@ -254,6 +254,14 @@ static void pcrypt_aead_exit_tfm(struct crypto_aead *tfm) 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 @@ static int pcrypt_create_aead(struct crypto_template *tmpl, struct rtattr **tb, 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 @@ static int pcrypt_create(struct crypto_template *tmpl, struct rtattr **tb) 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 void pcrypt_fini_padata(struct padata_pcrypt *pcrypt) static struct crypto_template pcrypt_tmpl = { .name = "pcrypt", .create = pcrypt_create, - .free = pcrypt_free, .module = THIS_MODULE, }; diff --git a/crypto/shash.c b/crypto/shash.c index 9ae1e891308d..8281d03b7cc2 100644 --- a/crypto/shash.c +++ b/crypto/shash.c @@ -24,11 +24,12 @@ static const struct crypto_type crypto_shash_type; -static int shash_no_setkey(struct crypto_shash *tfm, const u8 *key, - unsigned int keylen) +int shash_no_setkey(struct crypto_shash *tfm, const u8 *key, + unsigned int keylen) { return -ENOSYS; } +EXPORT_SYMBOL_GPL(shash_no_setkey); static int shash_setkey_unaligned(struct crypto_shash *tfm, const u8 *key, unsigned int keylen) diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index f0099360039e..42086ad535c5 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -82,7 +82,8 @@ static ssize_t driver_override_store(struct device *_dev, struct amba_device *dev = to_amba_device(_dev); char *driver_override, *old = dev->driver_override, *cp; - if (count > PATH_MAX) + /* We need to keep extra room for a newline */ + if (count >= (PAGE_SIZE - 1)) return -EINVAL; driver_override = kstrndup(buf, count, GFP_KERNEL); diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 5d32d5b0f23b..ee39c34f153c 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2101,6 +2101,26 @@ static void binder_send_failed_reply(struct binder_transaction *t, } } +/** + * binder_cleanup_transaction() - cleans up undelivered transaction + * @t: transaction that needs to be cleaned up + * @reason: reason the transaction wasn't delivered + * @error_code: error to return to caller (if synchronous call) + */ +static void binder_cleanup_transaction(struct binder_transaction *t, + const char *reason, + uint32_t error_code) +{ + if (t->buffer->target_node && !(t->flags & TF_ONE_WAY)) { + binder_send_failed_reply(t, error_code); + } else { + binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, + "undelivered transaction %d, %s\n", + t->debug_id, reason); + binder_free_transaction(t); + } +} + /** * binder_validate_object() - checks for a valid metadata object in a buffer. * @buffer: binder_buffer that we're parsing. @@ -3709,22 +3729,12 @@ static int binder_thread_write(struct binder_proc *proc, ref->death = death; if (ref->node->proc == NULL) { ref->death->work.type = BINDER_WORK_DEAD_BINDER; - if (thread->looper & - (BINDER_LOOPER_STATE_REGISTERED | - BINDER_LOOPER_STATE_ENTERED)) - binder_enqueue_work( - proc, - &ref->death->work, - &thread->todo); - else { - binder_inner_proc_lock(proc); - binder_enqueue_work_ilocked( - &ref->death->work, - &proc->todo); - binder_wakeup_proc_ilocked( - proc); - binder_inner_proc_unlock(proc); - } + + binder_inner_proc_lock(proc); + binder_enqueue_work_ilocked( + &ref->death->work, &proc->todo); + binder_wakeup_proc_ilocked(proc); + binder_inner_proc_unlock(proc); } } else { if (ref->death == NULL) { @@ -4198,12 +4208,20 @@ static int binder_thread_read(struct binder_proc *proc, if (put_user(cmd, (uint32_t __user *)ptr)) { if (t_from) binder_thread_dec_tmpref(t_from); + + binder_cleanup_transaction(t, "put_user failed", + BR_FAILED_REPLY); + return -EFAULT; } ptr += sizeof(uint32_t); if (copy_to_user(ptr, &tr, sizeof(tr))) { if (t_from) binder_thread_dec_tmpref(t_from); + + binder_cleanup_transaction(t, "copy_to_user failed", + BR_FAILED_REPLY); + return -EFAULT; } ptr += sizeof(tr); @@ -4273,15 +4291,9 @@ static void binder_release_work(struct binder_proc *proc, struct binder_transaction *t; t = container_of(w, struct binder_transaction, work); - if (t->buffer->target_node && - !(t->flags & TF_ONE_WAY)) { - binder_send_failed_reply(t, BR_DEAD_REPLY); - } else { - binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, - "undelivered transaction %d\n", - t->debug_id); - binder_free_transaction(t); - } + + binder_cleanup_transaction(t, "process died.", + BR_DEAD_REPLY); } break; case BINDER_WORK_RETURN_ERROR: { struct binder_error *e = container_of( diff --git a/drivers/block/loop.c b/drivers/block/loop.c index cec36d5c24f5..1c36de9719e5 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1569,9 +1569,8 @@ static int lo_open(struct block_device *bdev, fmode_t mode) return err; } -static void lo_release(struct gendisk *disk, fmode_t mode) +static void __lo_release(struct loop_device *lo) { - struct loop_device *lo = disk->private_data; int err; if (atomic_dec_return(&lo->lo_refcnt)) @@ -1597,6 +1596,13 @@ static void lo_release(struct gendisk *disk, fmode_t mode) mutex_unlock(&lo->lo_ctl_mutex); } +static void lo_release(struct gendisk *disk, fmode_t mode) +{ + mutex_lock(&loop_index_mutex); + __lo_release(disk->private_data); + mutex_unlock(&loop_index_mutex); +} + static const struct block_device_operations lo_fops = { .owner = THIS_MODULE, .open = lo_open, diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 80a5a44a1b35..e4c576303b83 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -56,7 +56,7 @@ #define ADSP_MMAP_HEAP_ADDR 4 #define ADSP_MMAP_REMOTE_HEAP_ADDR 8 #define FASTRPC_ENOSUCH 39 -#define VMID_SSC_Q6 5 +#define VMID_SSC_Q6 38 #define VMID_ADSP_Q6 6 #define AC_VM_ADSP_HEAP_SHARED 33 #define DEBUGFS_SIZE 1024 @@ -66,6 +66,8 @@ #define NUM_CHANNELS 4 /* adsp,sdsp,mdsp,cdsp */ #define NUM_SESSIONS 9 /*8 compute, 1 cpz*/ #define FASTRPC_CTX_MAGIC (0xbeeddeed) +#define FASTRPC_CTX_MAX (256) +#define FASTRPC_CTXID_MASK (0xFF0) #define IS_CACHE_ALIGNED(x) (((x) & ((L1_CACHE_BYTES)-1)) == 0) @@ -177,6 +179,7 @@ struct smq_invoke_ctx { struct overlap **overps; struct smq_msg msg; unsigned int magic; + uint64_t ctxid; }; struct fastrpc_ctx_lst { @@ -246,6 +249,8 @@ struct fastrpc_apps { struct ion_client *client; struct device *dev; bool glink; + spinlock_t ctxlock; + struct smq_invoke_ctx *ctxtable[FASTRPC_CTX_MAX]; }; struct fastrpc_mmap { @@ -909,7 +914,8 @@ static int context_alloc(struct fastrpc_file *fl, uint32_t kernel, struct fastrpc_ioctl_invoke_attrs *invokefd, struct smq_invoke_ctx **po) { - int err = 0, bufs, size = 0; + int err = 0, bufs, ii, size = 0; + struct fastrpc_apps *me = &gfa; struct smq_invoke_ctx *ctx = NULL; struct fastrpc_ctx_lst *clst = &fl->clst; struct fastrpc_ioctl_invoke *invoke = &invokefd->inv; @@ -969,6 +975,21 @@ static int context_alloc(struct fastrpc_file *fl, uint32_t kernel, hlist_add_head(&ctx->hn, &clst->pending); spin_unlock(&fl->hlock); + spin_lock(&me->ctxlock); + for (ii = 0; ii < FASTRPC_CTX_MAX; ii++) { + if (!me->ctxtable[ii]) { + me->ctxtable[ii] = ctx; + ctx->ctxid = (ptr_to_uint64(ctx) & ~0xFFF)|(ii << 4); + break; + } + } + spin_unlock(&me->ctxlock); + VERIFY(err, ii < FASTRPC_CTX_MAX); + if (err) { + pr_err("adsprpc: out of context memory\n"); + goto bail; + } + *po = ctx; bail: if (ctx && err) @@ -990,6 +1011,7 @@ static void context_save_interrupted(struct smq_invoke_ctx *ctx) static void context_free(struct smq_invoke_ctx *ctx) { int i; + struct fastrpc_apps *me = &gfa; int nbufs = REMOTE_SCALARS_INBUFS(ctx->sc) + REMOTE_SCALARS_OUTBUFS(ctx->sc); spin_lock(&ctx->fl->hlock); @@ -999,6 +1021,17 @@ static void context_free(struct smq_invoke_ctx *ctx) fastrpc_mmap_free(ctx->maps[i]); fastrpc_buf_free(ctx->buf, 1); ctx->magic = 0; + ctx->ctxid = 0; + + spin_lock(&me->ctxlock); + for (i = 0; i < FASTRPC_CTX_MAX; i++) { + if (me->ctxtable[i] == ctx) { + me->ctxtable[i] = NULL; + break; + } + } + spin_unlock(&me->ctxlock); + kfree(ctx); } @@ -1437,7 +1470,7 @@ static int fastrpc_invoke_send(struct smq_invoke_ctx *ctx, msg->tid = current->pid; if (kernel) msg->pid = 0; - msg->invoke.header.ctx = ptr_to_uint64(ctx) | fl->pd; + msg->invoke.header.ctx = ctx->ctxid | fl->pd; msg->invoke.header.handle = handle; msg->invoke.header.sc = ctx->sc; msg->invoke.page.addr = ctx->buf ? ctx->buf->phys : 0; @@ -1471,20 +1504,31 @@ static void fastrpc_smd_read_handler(int cid) { struct fastrpc_apps *me = &gfa; struct smq_invoke_rsp rsp = {0}; - struct smq_invoke_ctx *ctx; int ret = 0, err = 0; + uint32_t index; do { ret = smd_read_from_cb(me->channel[cid].chan, &rsp, sizeof(rsp)); if (ret != sizeof(rsp)) break; - ctx = (struct smq_invoke_ctx *)(uint64_to_ptr(rsp.ctx)); - VERIFY(err, (ctx && ctx->magic == FASTRPC_CTX_MAGIC)); + index = (uint32_t)((rsp.ctx & FASTRPC_CTXID_MASK) >> 4); + VERIFY(err, index < FASTRPC_CTX_MAX); + if (err) + goto bail; + + VERIFY(err, !IS_ERR_OR_NULL(me->ctxtable[index])); + if (err) + goto bail; + + VERIFY(err, ((me->ctxtable[index]->ctxid == (rsp.ctx & ~1)) && + me->ctxtable[index]->magic == FASTRPC_CTX_MAGIC)); if (err) goto bail; - context_notify_user(uint64_to_ptr(rsp.ctx), rsp.retval); + + context_notify_user(me->ctxtable[index], rsp.retval); } while (ret == sizeof(rsp)); + bail: if (err) pr_err("adsprpc: invalid response or context\n"); @@ -1514,6 +1558,7 @@ static void fastrpc_init(struct fastrpc_apps *me) INIT_HLIST_HEAD(&me->drivers); INIT_HLIST_HEAD(&me->maps); spin_lock_init(&me->hlock); + spin_lock_init(&me->ctxlock); mutex_init(&me->smd_mutex); me->channel = &gcinfo[0]; for (i = 0; i < NUM_CHANNELS; i++) { @@ -2172,14 +2217,32 @@ static void fastrpc_glink_notify_rx(void *handle, const void *priv, const void *pkt_priv, const void *ptr, size_t size) { struct smq_invoke_rsp *rsp = (struct smq_invoke_rsp *)ptr; - int len = size; + struct fastrpc_apps *me = &gfa; + uint32_t index; + int err = 0; - while (len >= sizeof(*rsp) && rsp) { - rsp->ctx = rsp->ctx & ~1; - context_notify_user(uint64_to_ptr(rsp->ctx), rsp->retval); - rsp++; - len = len - sizeof(*rsp); - } + VERIFY(err, (rsp && size >= sizeof(*rsp))); + if (err) + goto bail; + + index = (uint32_t)((rsp->ctx & FASTRPC_CTXID_MASK) >> 4); + VERIFY(err, index < FASTRPC_CTX_MAX); + if (err) + goto bail; + + VERIFY(err, !IS_ERR_OR_NULL(me->ctxtable[index])); + if (err) + goto bail; + + VERIFY(err, ((me->ctxtable[index]->ctxid == (rsp->ctx & ~1)) && + me->ctxtable[index]->magic == FASTRPC_CTX_MAGIC)); + if (err) + goto bail; + + context_notify_user(me->ctxtable[index], rsp->retval); +bail: + if (err) + pr_err("adsprpc: invalid response or context\n"); glink_rx_done(handle, ptr, true); } diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c index 196e87b61705..390c83719902 100644 --- a/drivers/char/diag/diag_dci.c +++ b/drivers/char/diag/diag_dci.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 @@ -688,7 +688,7 @@ int diag_dci_query_log_mask(struct diag_dci_client_tbl *entry, byte_mask = 0x01 << (item_num % 8); offset = equip_id * 514; - if (offset + byte_index > DCI_LOG_MASK_SIZE) { + if (offset + byte_index >= DCI_LOG_MASK_SIZE) { pr_err("diag: In %s, invalid offset: %d, log_code: %d, byte_index: %d\n", __func__, offset, log_code, byte_index); return 0; @@ -715,7 +715,7 @@ int diag_dci_query_event_mask(struct diag_dci_client_tbl *entry, bit_index = event_id % 8; byte_mask = 0x1 << bit_index; - if (byte_index > DCI_EVENT_MASK_SIZE) { + if (byte_index >= DCI_EVENT_MASK_SIZE) { pr_err("diag: In %s, invalid, event_id: %d, byte_index: %d\n", __func__, event_id, byte_index); return 0; @@ -863,7 +863,7 @@ static void dci_process_ctrl_status(unsigned char *buf, int len, int token) read_len += sizeof(struct diag_ctrl_dci_status); for (i = 0; i < header->count; i++) { - if (read_len > len) { + if (read_len > (len - 2)) { pr_err("diag: In %s, Invalid length len: %d\n", __func__, len); return; @@ -1162,18 +1162,31 @@ void extract_dci_events(unsigned char *buf, int len, int data_source, struct list_head *start, *temp; struct diag_dci_client_tbl *entry = NULL; - length = *(uint16_t *)(buf + 1); /* total length of event series */ - if (length == 0) { - pr_err("diag: Incoming dci event length is invalid\n"); + if (!buf) { + pr_err("diag: In %s buffer is NULL\n", __func__); return; } /* - * Move directly to the start of the event series. 1 byte for - * event code and 2 bytes for the length field. + * 1 byte for event code and 2 bytes for the length field. * The length field indicates the total length removing the cmd_code * and the lenght field. The event parsing in that case should happen * till the end. */ + if (len < 3) { + pr_err("diag: In %s invalid len: %d\n", __func__, len); + return; + } + length = *(uint16_t *)(buf + 1); /* total length of event series */ + if ((length == 0) || (len != (length + 3))) { + pr_err("diag: Incoming dci event length: %d is invalid\n", + length); + return; + } + /* + * Move directly to the start of the event series. + * The event parsing should happen from start of event + * series till the end. + */ temp_len = 3; while (temp_len < length) { event_id_packet = *(uint16_t *)(buf + temp_len); @@ -1190,30 +1203,60 @@ void extract_dci_events(unsigned char *buf, int len, int data_source, * necessary. */ timestamp_len = 8; - memcpy(timestamp, buf + temp_len + 2, timestamp_len); + if ((temp_len + timestamp_len + 2) <= len) + memcpy(timestamp, buf + temp_len + 2, + timestamp_len); + else { + pr_err("diag: Invalid length in %s, len: %d, temp_len: %d", + __func__, len, temp_len); + return; + } } /* 13th and 14th bit represent the payload length */ if (((event_id_packet & 0x6000) >> 13) == 3) { payload_len_field = 1; - payload_len = *(uint8_t *) + if ((temp_len + timestamp_len + 3) <= len) { + payload_len = *(uint8_t *) (buf + temp_len + 2 + timestamp_len); - if (payload_len < (MAX_EVENT_SIZE - 13)) { - /* copy the payload length and the payload */ + } else { + pr_err("diag: Invalid length in %s, len: %d, temp_len: %d", + __func__, len, temp_len); + return; + } + if ((payload_len < (MAX_EVENT_SIZE - 13)) && + ((temp_len + timestamp_len + payload_len + 3) <= len)) { + /* + * Copy the payload length and the payload + * after skipping temp_len bytes for already + * parsed packet, timestamp_len for timestamp + * buffer, 2 bytes for event_id_packet. + */ memcpy(event_data + 12, buf + temp_len + 2 + timestamp_len, 1); memcpy(event_data + 13, buf + temp_len + 2 + timestamp_len + 1, payload_len); } else { - pr_err("diag: event > %d, payload_len = %d\n", - (MAX_EVENT_SIZE - 13), payload_len); + pr_err("diag: event > %d, payload_len = %d, temp_len = %d\n", + (MAX_EVENT_SIZE - 13), payload_len, temp_len); return; } } else { payload_len_field = 0; payload_len = (event_id_packet & 0x6000) >> 13; - /* copy the payload */ - memcpy(event_data + 12, buf + temp_len + 2 + + /* + * Copy the payload after skipping temp_len bytes + * for already parsed packet, timestamp_len for + * timestamp buffer, 2 bytes for event_id_packet. + */ + if ((payload_len < (MAX_EVENT_SIZE - 12)) && + ((temp_len + timestamp_len + payload_len + 2) <= len)) + memcpy(event_data + 12, buf + temp_len + 2 + timestamp_len, payload_len); + else { + pr_err("diag: event > %d, payload_len = %d, temp_len = %d\n", + (MAX_EVENT_SIZE - 12), payload_len, temp_len); + return; + } } /* Before copying the data to userspace, check if we are still @@ -1337,19 +1380,19 @@ void extract_dci_log(unsigned char *buf, int len, int data_source, int token, pr_err("diag: In %s buffer is NULL\n", __func__); return; } - - /* The first six bytes for the incoming log packet contains - * Command code (2), the length of the packet (2) and the length - * of the log (2) + /* + * The first eight bytes for the incoming log packet contains + * Command code (2), the length of the packet (2), the length + * of the log (2) and log code (2) */ - log_code = *(uint16_t *)(buf + 6); - read_bytes += sizeof(uint16_t) + 6; - if (read_bytes > len) { - pr_err("diag: Invalid length in %s, len: %d, read: %d", - __func__, len, read_bytes); + if (len < 8) { + pr_err("diag: In %s invalid len: %d\n", __func__, len); return; } + log_code = *(uint16_t *)(buf + 6); + read_bytes += sizeof(uint16_t) + 6; + /* parse through log mask table of each client and check mask */ mutex_lock(&driver->dci_mutex); list_for_each_safe(start, temp, &driver->dci_client_list) { @@ -1376,6 +1419,10 @@ void extract_dci_ext_pkt(unsigned char *buf, int len, int data_source, pr_err("diag: In %s buffer is NULL\n", __func__); return; } + if (len < (EXT_HDR_LEN + sizeof(uint8_t))) { + pr_err("diag: In %s invalid len: %d\n", __func__, len); + return; + } version = *(uint8_t *)buf + 1; if (version < EXT_HDR_VERSION) { @@ -1387,10 +1434,6 @@ void extract_dci_ext_pkt(unsigned char *buf, int len, int data_source, pkt = buf + EXT_HDR_LEN; pkt_cmd_code = *(uint8_t *)pkt; len -= EXT_HDR_LEN; - if (len < 0) { - pr_err("diag: %s, Invalid length len: %d\n", __func__, len); - return; - } switch (pkt_cmd_code) { case LOG_CMD_CODE: diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c index e1e86f6e74dc..ad6805553998 100644 --- a/drivers/char/diag/diag_masks.c +++ b/drivers/char/diag/diag_masks.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 @@ -185,10 +185,11 @@ static void diag_send_log_mask_update(uint8_t peripheral, int equip_id) } mask_info->update_buf = temp; mask_info->update_buf_len = header_len + mask_size; + buf = temp; } memcpy(buf, &ctrl_pkt, header_len); - if (mask_size > 0) + if (mask_size > 0 && mask_size <= LOG_MASK_SIZE) memcpy(buf + header_len, mask->ptr, mask_size); mutex_unlock(&mask->lock); @@ -286,9 +287,16 @@ static void diag_send_event_mask_update(uint8_t peripheral) } else { mask_info->update_buf = temp; mask_info->update_buf_len = temp_len; + buf = temp; } } - memcpy(buf + sizeof(header), mask_info->ptr, num_bytes); + if (num_bytes > 0 && num_bytes < mask_info->mask_len) + memcpy(buf + sizeof(header), mask_info->ptr, num_bytes); + else { + pr_err("diag: num_bytes(%d) is not satisfying length condition\n", + num_bytes); + goto err; + } write_len += num_bytes; break; default: @@ -404,6 +412,7 @@ static void diag_send_msg_mask_update(uint8_t peripheral, int first, int last) } else { mask_info->update_buf = temp; mask_info->update_buf_len = temp_len; + buf = temp; pr_debug("diag: In %s, successfully reallocated msg_mask update buffer to len: %d\n", __func__, mask_info->update_buf_len); } @@ -537,8 +546,7 @@ static void diag_send_feature_mask_update(uint8_t peripheral) } static int diag_cmd_get_ssid_range(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i; int write_len = 0; @@ -546,23 +554,30 @@ static int diag_cmd_get_ssid_range(unsigned char *src_buf, int src_len, struct diag_msg_ssid_query_t rsp; struct diag_ssid_range_t ssid_range; struct diag_mask_info *mask_info = NULL; + struct diag_md_session_t *info = NULL; + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &msg_mask : info->msg_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || !mask_info) { pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } - if (!diag_apps_responds()) + if (!diag_apps_responds()) { + mutex_unlock(&driver->md_session_lock); return 0; + } mutex_lock(&driver->msg_mask_lock); rsp.cmd_code = DIAG_CMD_MSG_CONFIG; rsp.sub_cmd = DIAG_CMD_OP_GET_SSID_RANGE; @@ -584,12 +599,12 @@ static int diag_cmd_get_ssid_range(unsigned char *src_buf, int src_len, write_len += sizeof(ssid_range); } mutex_unlock(&driver->msg_mask_lock); + mutex_unlock(&driver->md_session_lock); return write_len; } static int diag_cmd_get_build_mask(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i = 0; int write_len = 0; @@ -642,8 +657,7 @@ static int diag_cmd_get_build_mask(unsigned char *src_buf, int src_len, } static int diag_cmd_get_msg_mask(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i; int write_len = 0; @@ -652,6 +666,10 @@ static int diag_cmd_get_msg_mask(unsigned char *src_buf, int src_len, struct diag_build_mask_req_t *req = NULL; struct diag_msg_build_mask_t rsp; struct diag_mask_info *mask_info = NULL; + struct diag_md_session_t *info = NULL; + + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &msg_mask : info->msg_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || @@ -659,15 +677,19 @@ static int diag_cmd_get_msg_mask(unsigned char *src_buf, int src_len, pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } - if (!diag_apps_responds()) + if (!diag_apps_responds()) { + mutex_unlock(&driver->md_session_lock); return 0; + } mutex_lock(&driver->msg_mask_lock); req = (struct diag_build_mask_req_t *)src_buf; @@ -682,6 +704,7 @@ static int diag_cmd_get_msg_mask(unsigned char *src_buf, int src_len, pr_err("diag: Invalid input in %s, mask->ptr: %pK\n", __func__, mask->ptr); mutex_unlock(&driver->msg_mask_lock); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) { @@ -701,12 +724,12 @@ static int diag_cmd_get_msg_mask(unsigned char *src_buf, int src_len, memcpy(dest_buf, &rsp, sizeof(rsp)); write_len += sizeof(rsp); mutex_unlock(&driver->msg_mask_lock); + mutex_unlock(&driver->md_session_lock); return write_len; } static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i; int write_len = 0; @@ -720,6 +743,10 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, struct diag_mask_info *mask_info = NULL; struct diag_msg_mask_t *mask_next = NULL; uint32_t *temp = NULL; + struct diag_md_session_t *info = NULL; + + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &msg_mask : info->msg_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || @@ -727,11 +754,13 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } @@ -744,6 +773,7 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, __func__, mask->ptr); mutex_unlock(&driver->msg_mask_lock); mutex_unlock(&mask_info->lock); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) { @@ -786,6 +816,7 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, mutex_unlock(&mask->lock); mutex_unlock(&driver->msg_mask_lock); mutex_unlock(&mask_info->lock); + mutex_unlock(&driver->md_session_lock); return -ENOMEM; } mask->ptr = temp; @@ -806,6 +837,7 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, } mutex_unlock(&driver->msg_mask_lock); mutex_unlock(&mask_info->lock); + mutex_unlock(&driver->md_session_lock); if (diag_check_update(APPS_DATA)) diag_update_userspace_clients(MSG_MASKS_TYPE); @@ -839,8 +871,7 @@ static int diag_cmd_set_msg_mask(unsigned char *src_buf, int src_len, } static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i; int write_len = 0; @@ -849,6 +880,10 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, struct diag_msg_config_rsp_t *req = NULL; struct diag_msg_mask_t *mask = NULL; struct diag_mask_info *mask_info = NULL; + struct diag_md_session_t *info = NULL; + + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &msg_mask : info->msg_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || @@ -856,11 +891,13 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } @@ -875,19 +912,22 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, __func__, mask->ptr); mutex_unlock(&driver->msg_mask_lock); mutex_unlock(&mask_info->lock); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } mask_info->status = (req->rt_mask) ? DIAG_CTRL_MASK_ALL_ENABLED : DIAG_CTRL_MASK_ALL_DISABLED; for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) { - mutex_lock(&mask->lock); - memset(mask->ptr, req->rt_mask, - mask->range * sizeof(uint32_t)); - mutex_unlock(&mask->lock); + if (mask && mask->ptr) { + mutex_lock(&mask->lock); + memset(mask->ptr, req->rt_mask, + mask->range * sizeof(uint32_t)); + mutex_unlock(&mask->lock); + } } mutex_unlock(&driver->msg_mask_lock); mutex_unlock(&mask_info->lock); - + mutex_unlock(&driver->md_session_lock); if (diag_check_update(APPS_DATA)) diag_update_userspace_clients(MSG_MASKS_TYPE); @@ -915,8 +955,7 @@ static int diag_cmd_set_all_msg_mask(unsigned char *src_buf, int src_len, } static int diag_cmd_get_event_mask(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int write_len = 0; uint32_t mask_size; @@ -951,8 +990,7 @@ static int diag_cmd_get_event_mask(unsigned char *src_buf, int src_len, } static int diag_cmd_update_event_mask(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i; int write_len = 0; @@ -961,18 +999,23 @@ static int diag_cmd_update_event_mask(unsigned char *src_buf, int src_len, struct diag_event_mask_config_t rsp; struct diag_event_mask_config_t *req; struct diag_mask_info *mask_info = NULL; + struct diag_md_session_t *info = NULL; + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &event_mask : info->event_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || !mask_info) { pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } req = (struct diag_event_mask_config_t *)src_buf; @@ -980,6 +1023,7 @@ static int diag_cmd_update_event_mask(unsigned char *src_buf, int src_len, if (mask_len <= 0 || mask_len > event_mask.mask_len) { pr_err("diag: In %s, invalid event mask len: %d\n", __func__, mask_len); + mutex_unlock(&driver->md_session_lock); return -EIO; } @@ -987,6 +1031,7 @@ static int diag_cmd_update_event_mask(unsigned char *src_buf, int src_len, memcpy(mask_info->ptr, src_buf + header_len, mask_len); mask_info->status = DIAG_CTRL_MASK_VALID; mutex_unlock(&mask_info->lock); + mutex_unlock(&driver->md_session_lock); if (diag_check_update(APPS_DATA)) diag_update_userspace_clients(EVENT_MASKS_TYPE); @@ -1015,26 +1060,30 @@ static int diag_cmd_update_event_mask(unsigned char *src_buf, int src_len, } static int diag_cmd_toggle_events(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i; int write_len = 0; uint8_t toggle = 0; struct diag_event_report_t header; struct diag_mask_info *mask_info = NULL; + struct diag_md_session_t *info = NULL; + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &event_mask : info->event_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || !mask_info) { pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } @@ -1048,6 +1097,7 @@ static int diag_cmd_toggle_events(unsigned char *src_buf, int src_len, memset(mask_info->ptr, 0, mask_info->mask_len); } mutex_unlock(&mask_info->lock); + mutex_unlock(&driver->md_session_lock); if (diag_check_update(APPS_DATA)) diag_update_userspace_clients(EVENT_MASKS_TYPE); @@ -1071,8 +1121,7 @@ static int diag_cmd_toggle_events(unsigned char *src_buf, int src_len, } static int diag_cmd_get_log_mask(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i; int status = LOG_STATUS_INVALID; @@ -1085,6 +1134,10 @@ static int diag_cmd_get_log_mask(unsigned char *src_buf, int src_len, struct diag_log_config_req_t *req; struct diag_log_config_rsp_t rsp; struct diag_mask_info *mask_info = NULL; + struct diag_md_session_t *info = NULL; + + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &log_mask : info->log_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || @@ -1092,16 +1145,20 @@ static int diag_cmd_get_log_mask(unsigned char *src_buf, int src_len, pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } - if (!diag_apps_responds()) + if (!diag_apps_responds()) { + mutex_unlock(&driver->md_session_lock); return 0; + } req = (struct diag_log_config_req_t *)src_buf; read_len += req_header_len; @@ -1121,6 +1178,7 @@ static int diag_cmd_get_log_mask(unsigned char *src_buf, int src_len, if (!log_item->ptr) { pr_err("diag: Invalid input in %s, mask: %pK\n", __func__, log_item); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } for (i = 0; i < MAX_EQUIP_ID; i++, log_item++) { @@ -1162,28 +1220,27 @@ static int diag_cmd_get_log_mask(unsigned char *src_buf, int src_len, rsp.status = status; memcpy(dest_buf, &rsp, rsp_header_len); + mutex_unlock(&driver->md_session_lock); return write_len; } static int diag_cmd_get_log_range(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { int i; int write_len = 0; struct diag_log_config_rsp_t rsp; - struct diag_mask_info *mask_info = NULL; struct diag_log_mask_t *mask = (struct diag_log_mask_t *)log_mask.ptr; + if (!mask) + return -EINVAL; + if (!diag_apps_responds()) return 0; - mask_info = (!info) ? &log_mask : info->log_mask; - if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || - !mask_info) { - pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", - __func__, src_buf, src_len, dest_buf, dest_len, - mask_info); + if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0) { + pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d\n", + __func__, src_buf, src_len, dest_buf, dest_len); return -EINVAL; } @@ -1206,7 +1263,7 @@ static int diag_cmd_get_log_range(unsigned char *src_buf, int src_len, static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + int pid) { int i; int write_len = 0; @@ -1221,6 +1278,10 @@ static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, struct diag_log_mask_t *mask = NULL; unsigned char *temp_buf = NULL; struct diag_mask_info *mask_info = NULL; + struct diag_md_session_t *info = NULL; + + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &log_mask : info->log_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || @@ -1228,11 +1289,13 @@ static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } @@ -1242,6 +1305,7 @@ static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, if (!mask->ptr) { pr_err("diag: Invalid input in %s, mask->ptr: %pK\n", __func__, mask->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (req->equip_id >= MAX_EQUIP_ID) { @@ -1258,6 +1322,8 @@ static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, mutex_lock(&mask_info->lock); for (i = 0; i < MAX_EQUIP_ID && !status; i++, mask++) { + if (!mask || !mask->ptr) + continue; if (mask->equip_id != req->equip_id) continue; mutex_lock(&mask->lock); @@ -1304,6 +1370,7 @@ static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, break; } mutex_unlock(&mask_info->lock); + mutex_unlock(&driver->md_session_lock); if (diag_check_update(APPS_DATA)) diag_update_userspace_clients(LOG_MASKS_TYPE); @@ -1344,14 +1411,16 @@ static int diag_cmd_set_log_mask(unsigned char *src_buf, int src_len, } static int diag_cmd_disable_log_mask(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) + unsigned char *dest_buf, int dest_len, int pid) { struct diag_mask_info *mask_info = NULL; struct diag_log_mask_t *mask = NULL; struct diag_log_config_rsp_t header; - int write_len = 0; - int i; + int write_len = 0, i; + struct diag_md_session_t *info = NULL; + + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); mask_info = (!info) ? &log_mask : info->log_mask; if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 || @@ -1359,25 +1428,31 @@ static int diag_cmd_disable_log_mask(unsigned char *src_buf, int src_len, pr_err("diag: Invalid input in %s, src_buf: %pK, src_len: %d, dest_buf: %pK, dest_len: %d, mask_info: %pK\n", __func__, src_buf, src_len, dest_buf, dest_len, mask_info); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (!mask_info->ptr) { pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n", __func__, mask_info->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } mask = (struct diag_log_mask_t *)mask_info->ptr; if (!mask->ptr) { pr_err("diag: Invalid input in %s, mask->ptr: %pK\n", __func__, mask->ptr); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } for (i = 0; i < MAX_EQUIP_ID; i++, mask++) { - mutex_lock(&mask->lock); - memset(mask->ptr, 0, mask->range); - mutex_unlock(&mask->lock); + if (mask && mask->ptr) { + mutex_lock(&mask->lock); + memset(mask->ptr, 0, mask->range); + mutex_unlock(&mask->lock); + } } mask_info->status = DIAG_CTRL_MASK_ALL_DISABLED; + mutex_unlock(&driver->md_session_lock); if (diag_check_update(APPS_DATA)) diag_update_userspace_clients(LOG_MASKS_TYPE); @@ -2109,14 +2184,12 @@ void diag_send_updates_peripheral(uint8_t peripheral) &driver->buffering_mode[peripheral]); } -int diag_process_apps_masks(unsigned char *buf, int len, - struct diag_md_session_t *info) +int diag_process_apps_masks(unsigned char *buf, int len, int pid) { int size = 0; int sub_cmd = 0; int (*hdlr)(unsigned char *src_buf, int src_len, - unsigned char *dest_buf, int dest_len, - struct diag_md_session_t *info) = NULL; + unsigned char *dest_buf, int dest_len, int pid) = NULL; if (!buf || len <= 0) return -EINVAL; @@ -2166,7 +2239,7 @@ int diag_process_apps_masks(unsigned char *buf, int len, if (hdlr) size = hdlr(buf, len, driver->apps_rsp_buf, - DIAG_MAX_RSP_SIZE, info); + DIAG_MAX_RSP_SIZE, pid); return (size > 0) ? size : 0; } diff --git a/drivers/char/diag/diag_masks.h b/drivers/char/diag/diag_masks.h index 1a52f946bb09..6edeee954d74 100644 --- a/drivers/char/diag/diag_masks.h +++ b/drivers/char/diag/diag_masks.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-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 @@ -167,8 +167,7 @@ int diag_event_mask_copy(struct diag_mask_info *dest, void diag_log_mask_free(struct diag_mask_info *mask_info); void diag_msg_mask_free(struct diag_mask_info *mask_info); void diag_event_mask_free(struct diag_mask_info *mask_info); -int diag_process_apps_masks(unsigned char *buf, int len, - struct diag_md_session_t *info); +int diag_process_apps_masks(unsigned char *buf, int len, int pid); void diag_send_updates_peripheral(uint8_t peripheral); extern int diag_create_msg_mask_table_entry(struct diag_msg_mask_t *msg_mask, diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c index 986aeed169f5..83a2c70fba02 100644 --- a/drivers/char/diag/diag_memorydevice.c +++ b/drivers/char/diag/diag_memorydevice.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 @@ -131,7 +131,7 @@ void diag_md_close_all() int diag_md_write(int id, unsigned char *buf, int len, int ctx) { - int i; + int i, pid = 0; uint8_t found = 0; unsigned long flags; struct diag_md_info *ch = NULL; @@ -149,10 +149,14 @@ int diag_md_write(int id, unsigned char *buf, int len, int ctx) if (peripheral < 0) return -EINVAL; - session_info = - diag_md_session_get_peripheral(peripheral); - if (!session_info) + mutex_lock(&driver->md_session_lock); + session_info = diag_md_session_get_peripheral(peripheral); + if (!session_info) { + mutex_unlock(&driver->md_session_lock); return -EIO; + } + pid = session_info->pid; + mutex_unlock(&driver->md_session_lock); ch = &diag_md[id]; if (!ch) @@ -195,13 +199,13 @@ int diag_md_write(int id, unsigned char *buf, int len, int ctx) found = 0; for (i = 0; i < driver->num_clients && !found; i++) { - if ((driver->client_map[i].pid != - session_info->pid) || + if ((driver->client_map[i].pid != pid) || (driver->client_map[i].pid == 0)) continue; found = 1; driver->data_ready[i] |= USER_SPACE_DATA_TYPE; + atomic_inc(&driver->data_ready_notif[i]); pr_debug("diag: wake up logging process\n"); wake_up_interruptible(&driver->wait_q); } diff --git a/drivers/char/diag/diag_usb.c b/drivers/char/diag/diag_usb.c index 0a0fc4400de5..87d021f6a956 100644 --- a/drivers/char/diag/diag_usb.c +++ b/drivers/char/diag/diag_usb.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-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 @@ -221,7 +221,7 @@ static void usb_disconnect(struct diag_usb_info *ch) if (!atomic_read(&ch->connected) && driver->usb_connected && diag_mask_param()) - diag_clear_masks(NULL); + diag_clear_masks(0); if (ch && ch->ops && ch->ops->close) ch->ops->close(ch->ctxt, DIAG_USB_MODE); diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h index d81a39e2c637..66d85eb2a026 100644 --- a/drivers/char/diag/diagchar.h +++ b/drivers/char/diag/diagchar.h @@ -26,6 +26,8 @@ #include #include "diagfwd_bridge.h" +#define THRESHOLD_CLIENT_LIMIT 50 + /* Size of the USB buffers used for read and write*/ #define USB_MAX_OUT_BUF 4096 #define APPS_BUF_SIZE 4096 @@ -508,6 +510,7 @@ struct diagchar_dev { wait_queue_head_t wait_q; struct diag_client_map *client_map; int *data_ready; + atomic_t data_ready_notif[THRESHOLD_CLIENT_LIMIT]; int num_clients; int polling_reg_flag; int use_device_tree; @@ -673,7 +676,7 @@ void diag_cmd_remove_reg_by_pid(int pid); void diag_cmd_remove_reg_by_proc(int proc); int diag_cmd_chk_polling(struct diag_cmd_reg_entry_t *entry); int diag_mask_param(void); -void diag_clear_masks(struct diag_md_session_t *info); +void diag_clear_masks(int pid); uint8_t diag_mask_to_pd_value(uint32_t peripheral_mask); void diag_record_stats(int type, int flag); diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index 4111e599877a..61f62cd49029 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.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 @@ -139,7 +139,6 @@ module_param(poolsize_qsc_usb, uint, 0); /* This is the max number of user-space clients supported at initialization*/ static unsigned int max_clients = 15; -static unsigned int threshold_client_limit = 50; module_param(max_clients, uint, 0); /* Timer variables */ @@ -171,7 +170,7 @@ uint16_t diag_debug_mask; void *diag_ipc_log; #endif -static void diag_md_session_close(struct diag_md_session_t *session_info); +static void diag_md_session_close(int pid); /* * Returns the next delayed rsp id. If wrapping is enabled, @@ -210,6 +209,16 @@ do { \ ret += length; \ } while (0) +#define COPY_USER_SPACE_OR_ERR(buf, data, length) \ +do { \ + if ((count < ret+length) || (copy_to_user(buf, \ + (void *)&data, length))) { \ + ret = -EFAULT; \ + break; \ + } \ + ret += length; \ +} while (0) + static void drain_timer_func(unsigned long data) { queue_work(driver->diag_wq , &(driver->diag_drain_work)); @@ -248,12 +257,13 @@ void diag_drain_work_fn(struct work_struct *work) timer_in_progress = 0; mutex_lock(&apps_data_mutex); + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_peripheral(APPS_DATA); if (session_info) hdlc_disabled = session_info->hdlc_disabled; else hdlc_disabled = driver->hdlc_disabled; - + mutex_unlock(&driver->md_session_lock); if (!hdlc_disabled) diag_drain_apps_data(&hdlc_data); else @@ -328,7 +338,7 @@ static int diagchar_open(struct inode *inode, struct file *file) if (i < driver->num_clients) { diag_add_client(i, file); } else { - if (i < threshold_client_limit) { + if (i < THRESHOLD_CLIENT_LIMIT) { driver->num_clients++; temp = krealloc(driver->client_map , (driver->num_clients) * sizeof(struct @@ -358,11 +368,17 @@ static int diagchar_open(struct inode *inode, struct file *file) } } driver->data_ready[i] = 0x0; + atomic_set(&driver->data_ready_notif[i], 0); driver->data_ready[i] |= MSG_MASKS_TYPE; + atomic_inc(&driver->data_ready_notif[i]); driver->data_ready[i] |= EVENT_MASKS_TYPE; + atomic_inc(&driver->data_ready_notif[i]); driver->data_ready[i] |= LOG_MASKS_TYPE; + atomic_inc(&driver->data_ready_notif[i]); driver->data_ready[i] |= DCI_LOG_MASKS_TYPE; + atomic_inc(&driver->data_ready_notif[i]); driver->data_ready[i] |= DCI_EVENT_MASKS_TYPE; + atomic_inc(&driver->data_ready_notif[i]); if (driver->ref_count == 0) diag_mempool_init(); @@ -373,8 +389,8 @@ static int diagchar_open(struct inode *inode, struct file *file) return -ENOMEM; fail: - mutex_unlock(&driver->diagchar_mutex); driver->num_clients--; + mutex_unlock(&driver->diagchar_mutex); pr_err_ratelimited("diag: Insufficient memory for new client"); return -ENOMEM; } @@ -433,7 +449,7 @@ int diag_mask_param(void) { return diag_mask_clear_param; } -void diag_clear_masks(struct diag_md_session_t *info) +void diag_clear_masks(int pid) { int ret; char cmd_disable_log_mask[] = { 0x73, 0, 0, 0, 0, 0, 0, 0}; @@ -442,14 +458,14 @@ void diag_clear_masks(struct diag_md_session_t *info) DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "diag: %s: masks clear request upon %s\n", __func__, - ((info) ? "ODL exit" : "USB Disconnection")); + ((pid) ? "ODL exit" : "USB Disconnection")); ret = diag_process_apps_masks(cmd_disable_log_mask, - sizeof(cmd_disable_log_mask), info); + sizeof(cmd_disable_log_mask), pid); ret = diag_process_apps_masks(cmd_disable_msg_mask, - sizeof(cmd_disable_msg_mask), info); + sizeof(cmd_disable_msg_mask), pid); ret = diag_process_apps_masks(cmd_disable_event_mask, - sizeof(cmd_disable_event_mask), info); + sizeof(cmd_disable_event_mask), pid); DIAG_LOG(DIAG_DEBUG_PERIPHERALS, "diag:%s: masks cleared successfully\n", __func__); } @@ -462,12 +478,17 @@ static void diag_close_logging_process(const int pid) struct diag_md_session_t *session_info = NULL; struct diag_logging_mode_param_t params; + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_pid(pid); - if (!session_info) + if (!session_info) { + mutex_unlock(&driver->md_session_lock); return; + } + session_mask = session_info->peripheral_mask; + mutex_unlock(&driver->md_session_lock); if (diag_mask_clear_param) - diag_clear_masks(session_info); + diag_clear_masks(pid); mutex_lock(&driver->diag_maskclear_mutex); driver->mask_clear = 1; @@ -475,9 +496,6 @@ static void diag_close_logging_process(const int pid) mutex_lock(&driver->diagchar_mutex); - session_mask = session_info->peripheral_mask; - diag_md_session_close(session_info); - p_mask = diag_translate_kernel_to_user_mask(session_mask); @@ -501,7 +519,9 @@ static void diag_close_logging_process(const int pid) } } } - + mutex_lock(&driver->md_session_lock); + diag_md_session_close(pid); + mutex_unlock(&driver->md_session_lock); diag_switch_logging(¶ms); mutex_unlock(&driver->diagchar_mutex); @@ -1035,11 +1055,13 @@ static int diag_send_raw_data_remote(int proc, void *buf, int len, if (driver->hdlc_encode_buf_len != 0) return -EAGAIN; + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_peripheral(APPS_DATA); if (session_info) hdlc_disabled = session_info->hdlc_disabled; else hdlc_disabled = driver->hdlc_disabled; + mutex_unlock(&driver->md_session_lock); if (hdlc_disabled) { if (len < 4) { pr_err("diag: In %s, invalid len: %d of non_hdlc pkt", @@ -1403,15 +1425,16 @@ int diag_md_session_create(int mode, int peripheral_mask, int proc) return err; } -static void diag_md_session_close(struct diag_md_session_t *session_info) +static void diag_md_session_close(int pid) { int i; uint8_t found = 0; + struct diag_md_session_t *session_info = NULL; + session_info = diag_md_session_get_pid(pid); if (!session_info) return; - mutex_lock(&driver->md_session_lock); for (i = 0; i < NUM_MD_SESSIONS; i++) { if (driver->md_session_map[i] != session_info) continue; @@ -1437,13 +1460,14 @@ static void diag_md_session_close(struct diag_md_session_t *session_info) driver->md_session_mode = (found) ? DIAG_MD_PERIPHERAL : DIAG_MD_NONE; kfree(session_info); session_info = NULL; - mutex_unlock(&driver->md_session_lock); DIAG_LOG(DIAG_DEBUG_USERSPACE, "cleared up session\n"); } struct diag_md_session_t *diag_md_session_get_pid(int pid) { int i; + if (pid <= 0) + return NULL; for (i = 0; i < NUM_MD_SESSIONS; i++) { if (driver->md_session_map[i] && driver->md_session_map[i]->pid == pid) @@ -1459,10 +1483,12 @@ struct diag_md_session_t *diag_md_session_get_peripheral(uint8_t peripheral) return driver->md_session_map[peripheral]; } -static int diag_md_peripheral_switch(struct diag_md_session_t *session_info, +static int diag_md_peripheral_switch(int pid, int peripheral_mask, int req_mode) { int i, bit = 0; + struct diag_md_session_t *session_info = NULL; + session_info = diag_md_session_get_pid(pid); if (!session_info) return -EINVAL; if (req_mode != DIAG_USB_MODE || req_mode != DIAG_MEMORY_DEVICE_MODE) @@ -1472,25 +1498,20 @@ static int diag_md_peripheral_switch(struct diag_md_session_t *session_info, * check that md_session_map for i == session_info, * if not then race condition occurred and bail */ - mutex_lock(&driver->md_session_lock); for (i = 0; i < NUM_MD_SESSIONS; i++) { bit = MD_PERIPHERAL_MASK(i) & peripheral_mask; if (!bit) continue; if (req_mode == DIAG_USB_MODE) { - if (driver->md_session_map[i] != session_info) { - mutex_unlock(&driver->md_session_lock); + if (driver->md_session_map[i] != session_info) return -EINVAL; - } driver->md_session_map[i] = NULL; driver->md_session_mask &= ~bit; session_info->peripheral_mask &= ~bit; } else { - if (driver->md_session_map[i] != NULL) { - mutex_unlock(&driver->md_session_lock); + if (driver->md_session_map[i] != NULL) return -EINVAL; - } driver->md_session_map[i] = session_info; driver->md_session_mask |= bit; session_info->peripheral_mask |= bit; @@ -1499,7 +1520,6 @@ static int diag_md_peripheral_switch(struct diag_md_session_t *session_info, } driver->md_session_mode = DIAG_MD_PERIPHERAL; - mutex_unlock(&driver->md_session_lock); DIAG_LOG(DIAG_DEBUG_USERSPACE, "Changed Peripherals:0x%x to mode:%d\n", peripheral_mask, req_mode); } @@ -1508,7 +1528,7 @@ static int diag_md_session_check(int curr_mode, int req_mode, const struct diag_logging_mode_param_t *param, uint8_t *change_mode) { - int i, bit = 0, err = 0; + int i, bit = 0, err = 0, peripheral_mask = 0; int change_mask = 0; struct diag_md_session_t *session_info = NULL; @@ -1532,12 +1552,13 @@ static int diag_md_session_check(int curr_mode, int req_mode, if (req_mode == DIAG_USB_MODE) { if (curr_mode == DIAG_USB_MODE) return 0; + mutex_lock(&driver->md_session_lock); if (driver->md_session_mode == DIAG_MD_NONE && driver->md_session_mask == 0 && driver->logging_mask) { *change_mode = 1; + mutex_unlock(&driver->md_session_lock); return 0; } - /* * curr_mode is either DIAG_MULTI_MODE or DIAG_MD_MODE * Check if requested peripherals are already in usb mode @@ -1549,8 +1570,10 @@ static int diag_md_session_check(int curr_mode, int req_mode, if (bit & driver->logging_mask) change_mask |= bit; } - if (!change_mask) + if (!change_mask) { + mutex_unlock(&driver->md_session_lock); return 0; + } /* * Change is needed. Check if this md_session has set all the @@ -1559,29 +1582,29 @@ static int diag_md_session_check(int curr_mode, int req_mode, * If this session owns all the requested peripherals, then * call function to switch the modes/masks for the md_session */ - mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_pid(current->tgid); - mutex_unlock(&driver->md_session_lock); - if (!session_info) { *change_mode = 1; + mutex_unlock(&driver->md_session_lock); return 0; } - if ((change_mask & session_info->peripheral_mask) + peripheral_mask = session_info->peripheral_mask; + if ((change_mask & peripheral_mask) != change_mask) { DIAG_LOG(DIAG_DEBUG_USERSPACE, "Another MD Session owns a requested peripheral\n"); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } *change_mode = 1; /* If all peripherals are being set to USB Mode, call close */ - if (~change_mask & session_info->peripheral_mask) { - err = diag_md_peripheral_switch(session_info, + if (~change_mask & peripheral_mask) { + err = diag_md_peripheral_switch(current->tgid, change_mask, DIAG_USB_MODE); } else - diag_md_session_close(session_info); - + diag_md_session_close(current->tgid); + mutex_unlock(&driver->md_session_lock); return err; } else if (req_mode == DIAG_MEMORY_DEVICE_MODE) { @@ -1590,21 +1613,23 @@ static int diag_md_session_check(int curr_mode, int req_mode, * been set. Check that requested peripherals already set are * owned by this md session */ - change_mask = driver->md_session_mask & param->peripheral_mask; mutex_lock(&driver->md_session_lock); + change_mask = driver->md_session_mask & param->peripheral_mask; session_info = diag_md_session_get_pid(current->tgid); - mutex_unlock(&driver->md_session_lock); if (session_info) { if ((session_info->peripheral_mask & change_mask) != change_mask) { DIAG_LOG(DIAG_DEBUG_USERSPACE, "Another MD Session owns a requested peripheral\n"); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } - err = diag_md_peripheral_switch(session_info, + err = diag_md_peripheral_switch(current->tgid, change_mask, DIAG_USB_MODE); + mutex_unlock(&driver->md_session_lock); } else { + mutex_unlock(&driver->md_session_lock); if (change_mask) { DIAG_LOG(DIAG_DEBUG_USERSPACE, "Another MD Session owns a requested peripheral\n"); @@ -1866,6 +1891,7 @@ static int diag_ioctl_lsm_deinit(void) } driver->data_ready[i] |= DEINIT_TYPE; + atomic_inc(&driver->data_ready_notif[i]); mutex_unlock(&driver->diagchar_mutex); wake_up_interruptible(&driver->wait_q); @@ -2059,19 +2085,17 @@ static int diag_ioctl_hdlc_toggle(unsigned long ioarg) { uint8_t hdlc_support; struct diag_md_session_t *session_info = NULL; - mutex_lock(&driver->md_session_lock); - session_info = diag_md_session_get_pid(current->tgid); - mutex_unlock(&driver->md_session_lock); if (copy_from_user(&hdlc_support, (void __user *)ioarg, sizeof(uint8_t))) return -EFAULT; mutex_lock(&driver->hdlc_disable_mutex); - if (session_info) { - mutex_lock(&driver->md_session_lock); + mutex_lock(&driver->md_session_lock); + session_info = diag_md_session_get_pid(current->tgid); + if (session_info) session_info->hdlc_disabled = hdlc_support; - mutex_unlock(&driver->md_session_lock); - } else + else driver->hdlc_disabled = hdlc_support; + mutex_unlock(&driver->md_session_lock); mutex_unlock(&driver->hdlc_disable_mutex); diag_update_md_clients(HDLC_SUPPORT_TYPE); @@ -2783,7 +2807,6 @@ static int diag_user_process_raw_data(const char __user *buf, int len) int remote_proc = 0; const int mempool = POOL_TYPE_COPY; unsigned char *user_space_data = NULL; - struct diag_md_session_t *info = NULL; if (!buf || len <= 0 || len > CALLBACK_BUF_SIZE) { pr_err_ratelimited("diag: In %s, invalid buf %pK len: %d\n", @@ -2834,13 +2857,11 @@ static int diag_user_process_raw_data(const char __user *buf, int len) } else { wait_event_interruptible(driver->wait_q, (driver->in_busy_pktdata == 0)); - mutex_lock(&driver->md_session_lock); - info = diag_md_session_get_pid(current->tgid); - mutex_unlock(&driver->md_session_lock); - ret = diag_process_apps_pkt(user_space_data, len, info); + ret = diag_process_apps_pkt(user_space_data, len, + current->tgid); if (ret == 1) diag_send_error_rsp((void *)(user_space_data), len, - info); + current->tgid); } fail: diagmem_free(driver, user_space_data, mempool); @@ -2906,24 +2927,25 @@ static int diag_user_process_userspace_data(const char __user *buf, int len) if (!remote_proc) { mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_pid(current->tgid); - mutex_unlock(&driver->md_session_lock); if (!session_info) { pr_err("diag:In %s request came from invalid md session pid:%d", __func__, current->tgid); + mutex_unlock(&driver->md_session_lock); return -EINVAL; } if (session_info) hdlc_disabled = session_info->hdlc_disabled; else hdlc_disabled = driver->hdlc_disabled; + mutex_unlock(&driver->md_session_lock); if (!hdlc_disabled) diag_process_hdlc_pkt((void *) (driver->user_space_data_buf), - len, session_info); + len, current->tgid); else diag_process_non_hdlc_pkt((char *) (driver->user_space_data_buf), - len, session_info); + len, current->tgid); return 0; } @@ -3000,11 +3022,13 @@ static int diag_user_process_apps_data(const char __user *buf, int len, mutex_lock(&apps_data_mutex); mutex_lock(&driver->hdlc_disable_mutex); + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_peripheral(APPS_DATA); if (session_info) hdlc_disabled = session_info->hdlc_disabled; else hdlc_disabled = driver->hdlc_disabled; + mutex_unlock(&driver->md_session_lock); if (hdlc_disabled) ret = diag_process_apps_data_non_hdlc(user_space_data, len, pkt_type); @@ -3029,16 +3053,6 @@ static int diag_user_process_apps_data(const char __user *buf, int len, return 0; } -static int check_data_ready(int index) -{ - int data_type = 0; - - mutex_lock(&driver->diagchar_mutex); - data_type = driver->data_ready[index]; - mutex_unlock(&driver->diagchar_mutex); - return data_type; -} - static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -3065,7 +3079,8 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, pr_err("diag: bad address from user side\n"); return -EFAULT; } - wait_event_interruptible(driver->wait_q, (check_data_ready(index)) > 0); + wait_event_interruptible(driver->wait_q, + atomic_read(&driver->data_ready_notif[index]) > 0); mutex_lock(&driver->diagchar_mutex); @@ -3076,32 +3091,40 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, /*Copy the type of data being passed*/ data_type = driver->data_ready[index] & USER_SPACE_DATA_TYPE; driver->data_ready[index] ^= USER_SPACE_DATA_TYPE; + atomic_dec(&driver->data_ready_notif[index]); COPY_USER_SPACE_OR_EXIT(buf, data_type, sizeof(int)); /* place holder for number of data field */ ret += sizeof(int); mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_pid(current->tgid); - mutex_unlock(&driver->md_session_lock); exit_stat = diag_md_copy_to_user(buf, &ret, count, session_info); + mutex_unlock(&driver->md_session_lock); goto exit; } else if (driver->data_ready[index] & USER_SPACE_DATA_TYPE) { /* In case, the thread wakes up and the logging mode is not memory device any more, the condition needs to be cleared */ driver->data_ready[index] ^= USER_SPACE_DATA_TYPE; + atomic_dec(&driver->data_ready_notif[index]); } if (driver->data_ready[index] & HDLC_SUPPORT_TYPE) { data_type = driver->data_ready[index] & HDLC_SUPPORT_TYPE; driver->data_ready[index] ^= HDLC_SUPPORT_TYPE; + atomic_dec(&driver->data_ready_notif[index]); COPY_USER_SPACE_OR_EXIT(buf, data_type, sizeof(int)); mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_pid(current->tgid); - mutex_unlock(&driver->md_session_lock); - if (session_info) - COPY_USER_SPACE_OR_EXIT(buf+4, + if (session_info) { + COPY_USER_SPACE_OR_ERR(buf+4, session_info->hdlc_disabled, sizeof(uint8_t)); + if (ret == -EFAULT) { + mutex_unlock(&driver->md_session_lock); + goto exit; + } + } + mutex_unlock(&driver->md_session_lock); goto exit; } @@ -3110,6 +3133,7 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, data_type = driver->data_ready[index] & DEINIT_TYPE; COPY_USER_SPACE_OR_EXIT(buf, data_type, 4); driver->data_ready[index] ^= DEINIT_TYPE; + atomic_dec(&driver->data_ready_notif[index]); mutex_unlock(&driver->diagchar_mutex); diag_remove_client_entry(file); return ret; @@ -3118,45 +3142,74 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, if (driver->data_ready[index] & MSG_MASKS_TYPE) { /*Copy the type of data being passed*/ data_type = driver->data_ready[index] & MSG_MASKS_TYPE; + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_peripheral(APPS_DATA); - COPY_USER_SPACE_OR_EXIT(buf, data_type, sizeof(int)); + COPY_USER_SPACE_OR_ERR(buf, data_type, sizeof(int)); + if (ret == -EFAULT) { + mutex_unlock(&driver->md_session_lock); + goto exit; + } write_len = diag_copy_to_user_msg_mask(buf + ret, count, session_info); + mutex_unlock(&driver->md_session_lock); if (write_len > 0) ret += write_len; driver->data_ready[index] ^= MSG_MASKS_TYPE; + atomic_dec(&driver->data_ready_notif[index]); goto exit; } if (driver->data_ready[index] & EVENT_MASKS_TYPE) { /*Copy the type of data being passed*/ data_type = driver->data_ready[index] & EVENT_MASKS_TYPE; + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_peripheral(APPS_DATA); - COPY_USER_SPACE_OR_EXIT(buf, data_type, 4); + COPY_USER_SPACE_OR_ERR(buf, data_type, 4); + if (ret == -EFAULT) { + mutex_unlock(&driver->md_session_lock); + goto exit; + } if (session_info && session_info->event_mask && session_info->event_mask->ptr) { - COPY_USER_SPACE_OR_EXIT(buf + sizeof(int), + COPY_USER_SPACE_OR_ERR(buf + sizeof(int), *(session_info->event_mask->ptr), session_info->event_mask->mask_len); + if (ret == -EFAULT) { + mutex_unlock(&driver->md_session_lock); + goto exit; + } } else { - COPY_USER_SPACE_OR_EXIT(buf + sizeof(int), + COPY_USER_SPACE_OR_ERR(buf + sizeof(int), *(event_mask.ptr), event_mask.mask_len); + if (ret == -EFAULT) { + mutex_unlock(&driver->md_session_lock); + goto exit; + } } + mutex_unlock(&driver->md_session_lock); driver->data_ready[index] ^= EVENT_MASKS_TYPE; + atomic_dec(&driver->data_ready_notif[index]); goto exit; } if (driver->data_ready[index] & LOG_MASKS_TYPE) { /*Copy the type of data being passed*/ data_type = driver->data_ready[index] & LOG_MASKS_TYPE; + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_peripheral(APPS_DATA); - COPY_USER_SPACE_OR_EXIT(buf, data_type, sizeof(int)); + COPY_USER_SPACE_OR_ERR(buf, data_type, sizeof(int)); + if (ret == -EFAULT) { + mutex_unlock(&driver->md_session_lock); + goto exit; + } write_len = diag_copy_to_user_log_mask(buf + ret, count, session_info); + mutex_unlock(&driver->md_session_lock); if (write_len > 0) ret += write_len; driver->data_ready[index] ^= LOG_MASKS_TYPE; + atomic_dec(&driver->data_ready_notif[index]); goto exit; } @@ -3168,6 +3221,7 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, *(driver->apps_req_buf), driver->apps_req_buf_len); driver->data_ready[index] ^= PKT_TYPE; + atomic_dec(&driver->data_ready_notif[index]); driver->in_busy_pktdata = 0; goto exit; } @@ -3179,6 +3233,7 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, COPY_USER_SPACE_OR_EXIT(buf+4, *(driver->dci_pkt_buf), driver->dci_pkt_length); driver->data_ready[index] ^= DCI_PKT_TYPE; + atomic_dec(&driver->data_ready_notif[index]); driver->in_busy_dcipktdata = 0; goto exit; } @@ -3191,6 +3246,7 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, COPY_USER_SPACE_OR_EXIT(buf + 8, (dci_ops_tbl[DCI_LOCAL_PROC]. event_mask_composite), DCI_EVENT_MASK_SIZE); driver->data_ready[index] ^= DCI_EVENT_MASKS_TYPE; + atomic_dec(&driver->data_ready_notif[index]); goto exit; } @@ -3202,6 +3258,7 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, COPY_USER_SPACE_OR_EXIT(buf+8, (dci_ops_tbl[DCI_LOCAL_PROC]. log_mask_composite), DCI_LOG_MASK_SIZE); driver->data_ready[index] ^= DCI_LOG_MASKS_TYPE; + atomic_dec(&driver->data_ready_notif[index]); goto exit; } @@ -3233,6 +3290,7 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count, exit_stat = diag_copy_dci(buf, count, entry, &ret); mutex_lock(&driver->diagchar_mutex); driver->data_ready[index] ^= DCI_DATA_TYPE; + atomic_dec(&driver->data_ready_notif[index]); mutex_unlock(&driver->diagchar_mutex); if (exit_stat == 1) { mutex_unlock(&driver->dci_mutex); diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c index ef08f939c36e..53846d1bef90 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 @@ -48,6 +48,7 @@ #define STM_RSP_SUPPORTED_INDEX 7 #define STM_RSP_STATUS_INDEX 8 #define STM_RSP_NUM_BYTES 9 +#define RETRY_MAX_COUNT 1000 static int timestamp_switch; module_param(timestamp_switch, int, 0644); @@ -226,6 +227,7 @@ void chk_logging_wakeup(void) * situation. */ driver->data_ready[i] |= USER_SPACE_DATA_TYPE; + atomic_inc(&driver->data_ready_notif[i]); pr_debug("diag: Force wakeup of logging process\n"); wake_up_interruptible(&driver->wait_q); break; @@ -241,7 +243,7 @@ void chk_logging_wakeup(void) } static void pack_rsp_and_send(unsigned char *buf, int len, - struct diag_md_session_t *info) + int pid) { int err; int retry_count = 0, i, rsp_ctxt; @@ -249,6 +251,7 @@ static void pack_rsp_and_send(unsigned char *buf, int len, unsigned long flags; unsigned char *rsp_ptr = driver->encoded_rsp_buf; struct diag_pkt_frame_t header; + struct diag_md_session_t *session_info = NULL, *info = NULL; if (!rsp_ptr || !buf) return; @@ -259,28 +262,28 @@ static void pack_rsp_and_send(unsigned char *buf, int len, return; } + mutex_lock(&driver->md_session_lock); + session_info = diag_md_session_get_pid(pid); + info = (session_info) ? session_info : + diag_md_session_get_peripheral(APPS_DATA); + if (info && info->peripheral_mask) { - if (info->peripheral_mask == DIAG_CON_ALL || - (info->peripheral_mask & (1 << APPS_DATA)) || - (info->peripheral_mask & (1 << PERIPHERAL_MODEM))) { - rsp_ctxt = SET_BUF_CTXT(APPS_DATA, TYPE_CMD, 1); - } else { - for (i = 0; i <= NUM_PERIPHERALS; i++) { - if (info->peripheral_mask & (1 << i)) - break; - } - rsp_ctxt = SET_BUF_CTXT(i, TYPE_CMD, 1); + for (i = 0; i < NUM_MD_SESSIONS; i++) { + if (info->peripheral_mask & (1 << i)) + break; } + rsp_ctxt = SET_BUF_CTXT(i, TYPE_CMD, TYPE_CMD); } else rsp_ctxt = driver->rsp_buf_ctxt; + mutex_unlock(&driver->md_session_lock); /* * Keep trying till we get the buffer back. It should probably - * take one or two iterations. When this loops till UINT_MAX, it + * take one or two iterations. When this loops till RETRY_MAX_COUNT, it * means we did not get a write complete for the previous * response. */ - while (retry_count < UINT_MAX) { + while (retry_count < RETRY_MAX_COUNT) { if (!driver->rsp_buf_busy) break; /* @@ -296,8 +299,11 @@ static void pack_rsp_and_send(unsigned char *buf, int len, * draining responses when we are in Memory Device Mode. */ if (driver->logging_mode == DIAG_MEMORY_DEVICE_MODE || - driver->logging_mode == DIAG_MULTI_MODE) + driver->logging_mode == DIAG_MULTI_MODE) { + mutex_lock(&driver->md_session_lock); chk_logging_wakeup(); + mutex_unlock(&driver->md_session_lock); + } } if (driver->rsp_buf_busy) { pr_err("diag: unable to get hold of response buffer\n"); @@ -326,13 +332,14 @@ static void pack_rsp_and_send(unsigned char *buf, int len, } static void encode_rsp_and_send(unsigned char *buf, int len, - struct diag_md_session_t *info) + int pid) { struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 }; struct diag_hdlc_dest_type enc = { NULL, NULL, 0 }; unsigned char *rsp_ptr = driver->encoded_rsp_buf; int err, i, rsp_ctxt, retry_count = 0; unsigned long flags; + struct diag_md_session_t *session_info = NULL, *info = NULL; if (!rsp_ptr || !buf) return; @@ -343,28 +350,27 @@ static void encode_rsp_and_send(unsigned char *buf, int len, return; } + mutex_lock(&driver->md_session_lock); + session_info = diag_md_session_get_pid(pid); + info = (session_info) ? session_info : + diag_md_session_get_peripheral(APPS_DATA); + if (info && info->peripheral_mask) { - if (info->peripheral_mask == DIAG_CON_ALL || - (info->peripheral_mask & (1 << APPS_DATA)) || - (info->peripheral_mask & (1 << PERIPHERAL_MODEM))) { - rsp_ctxt = SET_BUF_CTXT(APPS_DATA, TYPE_CMD, 1); - } else { - for (i = 0; i <= NUM_PERIPHERALS; i++) { - if (info->peripheral_mask & (1 << i)) - break; - } - rsp_ctxt = SET_BUF_CTXT(i, TYPE_CMD, 1); + for (i = 0; i < NUM_MD_SESSIONS; i++) { + if (info->peripheral_mask & (1 << i)) + break; } + rsp_ctxt = SET_BUF_CTXT(i, TYPE_CMD, TYPE_CMD); } else rsp_ctxt = driver->rsp_buf_ctxt; - + mutex_unlock(&driver->md_session_lock); /* * Keep trying till we get the buffer back. It should probably - * take one or two iterations. When this loops till UINT_MAX, it + * take one or two iterations. When this loops till RETRY_MAX_COUNT, it * means we did not get a write complete for the previous * response. */ - while (retry_count < UINT_MAX) { + while (retry_count < RETRY_MAX_COUNT) { if (!driver->rsp_buf_busy) break; /* @@ -380,8 +386,11 @@ static void encode_rsp_and_send(unsigned char *buf, int len, * draining responses when we are in Memory Device Mode. */ if (driver->logging_mode == DIAG_MEMORY_DEVICE_MODE || - driver->logging_mode == DIAG_MULTI_MODE) + driver->logging_mode == DIAG_MULTI_MODE) { + mutex_lock(&driver->md_session_lock); chk_logging_wakeup(); + mutex_unlock(&driver->md_session_lock); + } } if (driver->rsp_buf_busy) { @@ -412,22 +421,23 @@ static void encode_rsp_and_send(unsigned char *buf, int len, memset(buf, '\0', DIAG_MAX_RSP_SIZE); } -void diag_send_rsp(unsigned char *buf, int len, struct diag_md_session_t *info) +static void diag_send_rsp(unsigned char *buf, int len, int pid) { - struct diag_md_session_t *session_info = NULL; + struct diag_md_session_t *session_info = NULL, *info = NULL; uint8_t hdlc_disabled; - + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); session_info = (info) ? info : diag_md_session_get_peripheral(APPS_DATA); if (session_info) hdlc_disabled = session_info->hdlc_disabled; else hdlc_disabled = driver->hdlc_disabled; - + mutex_unlock(&driver->md_session_lock); if (hdlc_disabled) - pack_rsp_and_send(buf, len, session_info); + pack_rsp_and_send(buf, len, pid); else - encode_rsp_and_send(buf, len, session_info); + encode_rsp_and_send(buf, len, pid); } void diag_update_pkt_buffer(unsigned char *buf, uint32_t len, int type) @@ -480,8 +490,10 @@ void diag_update_userspace_clients(unsigned int type) mutex_lock(&driver->diagchar_mutex); for (i = 0; i < driver->num_clients; i++) - if (driver->client_map[i].pid != 0) + if (driver->client_map[i].pid != 0) { driver->data_ready[i] |= type; + atomic_inc(&driver->data_ready_notif[i]); + } wake_up_interruptible(&driver->wait_q); mutex_unlock(&driver->diagchar_mutex); } @@ -491,6 +503,7 @@ void diag_update_md_clients(unsigned int type) int i, j; mutex_lock(&driver->diagchar_mutex); + mutex_lock(&driver->md_session_lock); for (i = 0; i < NUM_MD_SESSIONS; i++) { if (driver->md_session_map[i] != NULL) for (j = 0; j < driver->num_clients; j++) { @@ -498,10 +511,13 @@ void diag_update_md_clients(unsigned int type) driver->client_map[j].pid == driver->md_session_map[i]->pid) { driver->data_ready[j] |= type; + atomic_inc( + &driver->data_ready_notif[j]); break; } } } + mutex_unlock(&driver->md_session_lock); wake_up_interruptible(&driver->wait_q); mutex_unlock(&driver->diagchar_mutex); } @@ -513,6 +529,7 @@ void diag_update_sleeping_process(int process_id, int data_type) for (i = 0; i < driver->num_clients; i++) if (driver->client_map[i].pid == process_id) { driver->data_ready[i] |= data_type; + atomic_inc(&driver->data_ready_notif[i]); break; } wake_up_interruptible(&driver->wait_q); @@ -899,7 +916,7 @@ static int diag_cmd_disable_hdlc(unsigned char *src_buf, int src_len, } void diag_send_error_rsp(unsigned char *buf, int len, - struct diag_md_session_t *info) + int pid) { /* -1 to accomodate the first byte 0x13 */ if (len > (DIAG_MAX_RSP_SIZE - 1)) { @@ -909,27 +926,27 @@ void diag_send_error_rsp(unsigned char *buf, int len, *(uint8_t *)driver->apps_rsp_buf = DIAG_CMD_ERROR; memcpy((driver->apps_rsp_buf + sizeof(uint8_t)), buf, len); - diag_send_rsp(driver->apps_rsp_buf, len + 1, info); + diag_send_rsp(driver->apps_rsp_buf, len + 1, pid); } -int diag_process_apps_pkt(unsigned char *buf, int len, - struct diag_md_session_t *info) +int diag_process_apps_pkt(unsigned char *buf, int len, int pid) { - int i; + int i, p_mask = 0; int mask_ret; int write_len = 0; unsigned char *temp = NULL; struct diag_cmd_reg_entry_t entry; struct diag_cmd_reg_entry_t *temp_entry = NULL; struct diag_cmd_reg_t *reg_item = NULL; + struct diag_md_session_t *info = NULL; if (!buf) return -EIO; /* Check if the command is a supported mask command */ - mask_ret = diag_process_apps_masks(buf, len, info); + mask_ret = diag_process_apps_masks(buf, len, pid); if (mask_ret > 0) { - diag_send_rsp(driver->apps_rsp_buf, mask_ret, info); + diag_send_rsp(driver->apps_rsp_buf, mask_ret, pid); return 0; } @@ -951,7 +968,7 @@ int diag_process_apps_pkt(unsigned char *buf, int len, driver->apps_rsp_buf, DIAG_MAX_RSP_SIZE); if (write_len > 0) - diag_send_rsp(driver->apps_rsp_buf, write_len, info); + diag_send_rsp(driver->apps_rsp_buf, write_len, pid); return 0; } @@ -960,14 +977,18 @@ int diag_process_apps_pkt(unsigned char *buf, int len, if (temp_entry) { reg_item = container_of(temp_entry, struct diag_cmd_reg_t, entry); + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); if (info) { - if (MD_PERIPHERAL_MASK(reg_item->proc) & - info->peripheral_mask) + p_mask = info->peripheral_mask; + mutex_unlock(&driver->md_session_lock); + if (MD_PERIPHERAL_MASK(reg_item->proc) & p_mask) write_len = diag_send_data(reg_item, buf, len); } else { + mutex_unlock(&driver->md_session_lock); if (MD_PERIPHERAL_MASK(reg_item->proc) & driver->logging_mask) - diag_send_error_rsp(buf, len, info); + diag_send_error_rsp(buf, len, pid); else write_len = diag_send_data(reg_item, buf, len); } @@ -983,13 +1004,13 @@ int diag_process_apps_pkt(unsigned char *buf, int len, for (i = 0; i < 4; i++) *(driver->apps_rsp_buf+i) = *(buf+i); *(uint32_t *)(driver->apps_rsp_buf+4) = DIAG_MAX_REQ_SIZE; - diag_send_rsp(driver->apps_rsp_buf, 8, info); + diag_send_rsp(driver->apps_rsp_buf, 8, pid); return 0; } else if ((*buf == 0x4b) && (*(buf+1) == 0x12) && (*(uint16_t *)(buf+2) == DIAG_DIAG_STM)) { len = diag_process_stm_cmd(buf, driver->apps_rsp_buf); if (len > 0) { - diag_send_rsp(driver->apps_rsp_buf, len, info); + diag_send_rsp(driver->apps_rsp_buf, len, pid); return 0; } return len; @@ -1002,7 +1023,7 @@ int diag_process_apps_pkt(unsigned char *buf, int len, driver->apps_rsp_buf, DIAG_MAX_RSP_SIZE); if (write_len > 0) - diag_send_rsp(driver->apps_rsp_buf, write_len, info); + diag_send_rsp(driver->apps_rsp_buf, write_len, pid); return 0; } /* Check for time sync switch command */ @@ -1013,14 +1034,14 @@ int diag_process_apps_pkt(unsigned char *buf, int len, driver->apps_rsp_buf, DIAG_MAX_RSP_SIZE); if (write_len > 0) - diag_send_rsp(driver->apps_rsp_buf, write_len, info); + diag_send_rsp(driver->apps_rsp_buf, write_len, pid); return 0; } /* Check for download command */ else if ((chk_apps_master()) && (*buf == 0x3A)) { /* send response back */ driver->apps_rsp_buf[0] = *buf; - diag_send_rsp(driver->apps_rsp_buf, 1, info); + diag_send_rsp(driver->apps_rsp_buf, 1, pid); msleep(5000); /* call download API */ msm_set_restart_mode(RESTART_DLOAD); @@ -1040,7 +1061,7 @@ int diag_process_apps_pkt(unsigned char *buf, int len, for (i = 0; i < 13; i++) driver->apps_rsp_buf[i+3] = 0; - diag_send_rsp(driver->apps_rsp_buf, 16, info); + diag_send_rsp(driver->apps_rsp_buf, 16, pid); return 0; } } @@ -1049,7 +1070,7 @@ int diag_process_apps_pkt(unsigned char *buf, int len, (*(buf+2) == 0x04) && (*(buf+3) == 0x0)) { memcpy(driver->apps_rsp_buf, buf, 4); driver->apps_rsp_buf[4] = wrap_enabled; - diag_send_rsp(driver->apps_rsp_buf, 5, info); + diag_send_rsp(driver->apps_rsp_buf, 5, pid); return 0; } /* Wrap the Delayed Rsp ID */ @@ -1058,7 +1079,7 @@ int diag_process_apps_pkt(unsigned char *buf, int len, wrap_enabled = true; memcpy(driver->apps_rsp_buf, buf, 4); driver->apps_rsp_buf[4] = wrap_count; - diag_send_rsp(driver->apps_rsp_buf, 6, info); + diag_send_rsp(driver->apps_rsp_buf, 6, pid); return 0; } /* Mobile ID Rsp */ @@ -1069,7 +1090,7 @@ int diag_process_apps_pkt(unsigned char *buf, int len, driver->apps_rsp_buf, DIAG_MAX_RSP_SIZE); if (write_len > 0) { - diag_send_rsp(driver->apps_rsp_buf, write_len, info); + diag_send_rsp(driver->apps_rsp_buf, write_len, pid); return 0; } } @@ -1089,7 +1110,7 @@ int diag_process_apps_pkt(unsigned char *buf, int len, for (i = 0; i < 55; i++) driver->apps_rsp_buf[i] = 0; - diag_send_rsp(driver->apps_rsp_buf, 55, info); + diag_send_rsp(driver->apps_rsp_buf, 55, pid); return 0; } /* respond to 0x7c command */ @@ -1102,14 +1123,14 @@ int diag_process_apps_pkt(unsigned char *buf, int len, chk_config_get_id(); *(unsigned char *)(driver->apps_rsp_buf + 12) = '\0'; *(unsigned char *)(driver->apps_rsp_buf + 13) = '\0'; - diag_send_rsp(driver->apps_rsp_buf, 14, info); + diag_send_rsp(driver->apps_rsp_buf, 14, pid); return 0; } } write_len = diag_cmd_chk_stats(buf, len, driver->apps_rsp_buf, DIAG_MAX_RSP_SIZE); if (write_len > 0) { - diag_send_rsp(driver->apps_rsp_buf, write_len, info); + diag_send_rsp(driver->apps_rsp_buf, write_len, pid); return 0; } write_len = diag_cmd_disable_hdlc(buf, len, driver->apps_rsp_buf, @@ -1121,7 +1142,7 @@ int diag_process_apps_pkt(unsigned char *buf, int len, * before disabling HDLC encoding on Apps processor. */ mutex_lock(&driver->hdlc_disable_mutex); - diag_send_rsp(driver->apps_rsp_buf, write_len, info); + diag_send_rsp(driver->apps_rsp_buf, write_len, pid); /* * Set the value of hdlc_disabled after sending the response to * the tools. This is required since the tools is expecting a @@ -1129,10 +1150,13 @@ int diag_process_apps_pkt(unsigned char *buf, int len, */ pr_debug("diag: In %s, disabling HDLC encoding\n", __func__); + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); if (info) info->hdlc_disabled = 1; else driver->hdlc_disabled = 1; + mutex_unlock(&driver->md_session_lock); diag_update_md_clients(HDLC_SUPPORT_TYPE); mutex_unlock(&driver->hdlc_disable_mutex); return 0; @@ -1141,13 +1165,12 @@ int diag_process_apps_pkt(unsigned char *buf, int len, /* We have now come to the end of the function. */ if (chk_apps_only()) - diag_send_error_rsp(buf, len, info); + diag_send_error_rsp(buf, len, pid); return 0; } -void diag_process_hdlc_pkt(void *data, unsigned len, - struct diag_md_session_t *info) +void diag_process_hdlc_pkt(void *data, unsigned int len, int pid) { int err = 0; int ret = 0; @@ -1207,7 +1230,7 @@ void diag_process_hdlc_pkt(void *data, unsigned len, } err = diag_process_apps_pkt(driver->hdlc_buf, - driver->hdlc_buf_len, info); + driver->hdlc_buf_len, pid); if (err < 0) goto fail; } else { @@ -1224,7 +1247,7 @@ void diag_process_hdlc_pkt(void *data, unsigned len, * recovery algorithm. Send an error response if the * packet is not in expected format. */ - diag_send_error_rsp(driver->hdlc_buf, driver->hdlc_buf_len, info); + diag_send_error_rsp(driver->hdlc_buf, driver->hdlc_buf_len, pid); driver->hdlc_buf_len = 0; end: mutex_unlock(&driver->diag_hdlc_mutex); @@ -1321,9 +1344,11 @@ static int diagfwd_mux_close(int id, int mode) static uint8_t hdlc_reset; -static void hdlc_reset_timer_start(struct diag_md_session_t *info) +static void hdlc_reset_timer_start(int pid) { + struct diag_md_session_t *info = NULL; mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); if (!hdlc_timer_in_progress) { hdlc_timer_in_progress = 1; if (info) @@ -1365,15 +1390,16 @@ void diag_md_hdlc_reset_timer_func(unsigned long pid) } static void diag_hdlc_start_recovery(unsigned char *buf, int len, - struct diag_md_session_t *info) + int pid) { int i; static uint32_t bad_byte_counter; unsigned char *start_ptr = NULL; struct diag_pkt_frame_t *actual_pkt = NULL; + struct diag_md_session_t *info = NULL; hdlc_reset = 1; - hdlc_reset_timer_start(info); + hdlc_reset_timer_start(pid); actual_pkt = (struct diag_pkt_frame_t *)buf; for (i = 0; i < len; i++) { @@ -1392,10 +1418,13 @@ static void diag_hdlc_start_recovery(unsigned char *buf, int len, pr_err("diag: In %s, re-enabling HDLC encoding\n", __func__); mutex_lock(&driver->hdlc_disable_mutex); + mutex_lock(&driver->md_session_lock); + info = diag_md_session_get_pid(pid); if (info) info->hdlc_disabled = 0; else driver->hdlc_disabled = 0; + mutex_unlock(&driver->md_session_lock); mutex_unlock(&driver->hdlc_disable_mutex); diag_update_md_clients(HDLC_SUPPORT_TYPE); @@ -1408,12 +1437,11 @@ static void diag_hdlc_start_recovery(unsigned char *buf, int len, mutex_lock(&driver->hdlc_recovery_mutex); driver->incoming_pkt.processing = 0; mutex_unlock(&driver->hdlc_recovery_mutex); - diag_process_non_hdlc_pkt(start_ptr, len - i, info); + diag_process_non_hdlc_pkt(start_ptr, len - i, pid); } } -void diag_process_non_hdlc_pkt(unsigned char *buf, int len, - struct diag_md_session_t *info) +void diag_process_non_hdlc_pkt(unsigned char *buf, int len, int pid) { int err = 0; uint16_t pkt_len = 0; @@ -1469,11 +1497,11 @@ void diag_process_non_hdlc_pkt(unsigned char *buf, int len, if (*(uint8_t *)(data_ptr + actual_pkt->length) != CONTROL_CHAR) { mutex_unlock(&driver->hdlc_recovery_mutex); - diag_hdlc_start_recovery(buf, len, info); + diag_hdlc_start_recovery(buf, len, pid); mutex_lock(&driver->hdlc_recovery_mutex); } err = diag_process_apps_pkt(data_ptr, - actual_pkt->length, info); + actual_pkt->length, pid); if (err) { pr_err("diag: In %s, unable to process incoming data packet, err: %d\n", __func__, err); @@ -1495,8 +1523,8 @@ void diag_process_non_hdlc_pkt(unsigned char *buf, int len, pkt_len = actual_pkt->length; if (actual_pkt->start != CONTROL_CHAR) { - diag_hdlc_start_recovery(buf, len, info); - diag_send_error_rsp(buf, len, info); + diag_hdlc_start_recovery(buf, len, pid); + diag_send_error_rsp(buf, len, pid); goto end; } mutex_lock(&driver->hdlc_recovery_mutex); @@ -1504,7 +1532,7 @@ void diag_process_non_hdlc_pkt(unsigned char *buf, int len, pr_err("diag: In %s, incoming data is too large for the request buffer %d\n", __func__, pkt_len); mutex_unlock(&driver->hdlc_recovery_mutex); - diag_hdlc_start_recovery(buf, len, info); + diag_hdlc_start_recovery(buf, len, pid); break; } if ((pkt_len + header_len) > (len - read_bytes)) { @@ -1521,13 +1549,13 @@ void diag_process_non_hdlc_pkt(unsigned char *buf, int len, if (*(uint8_t *)(data_ptr + actual_pkt->length) != CONTROL_CHAR) { mutex_unlock(&driver->hdlc_recovery_mutex); - diag_hdlc_start_recovery(buf, len, info); + diag_hdlc_start_recovery(buf, len, pid); mutex_lock(&driver->hdlc_recovery_mutex); } else hdlc_reset = 0; err = diag_process_apps_pkt(data_ptr, - actual_pkt->length, info); + actual_pkt->length, pid); if (err) { mutex_unlock(&driver->hdlc_recovery_mutex); break; @@ -1546,9 +1574,9 @@ static int diagfwd_mux_read_done(unsigned char *buf, int len, int ctxt) return -EINVAL; if (!driver->hdlc_disabled) - diag_process_hdlc_pkt(buf, len, NULL); + diag_process_hdlc_pkt(buf, len, 0); else - diag_process_non_hdlc_pkt(buf, len, NULL); + diag_process_non_hdlc_pkt(buf, len, 0); diag_mux_queue_read(ctxt); return 0; @@ -1587,11 +1615,18 @@ static int diagfwd_mux_write_done(unsigned char *buf, int len, int buf_ctxt, } break; case TYPE_CMD: - if (peripheral >= 0 && peripheral < NUM_PERIPHERALS) { + if (peripheral >= 0 && peripheral < NUM_PERIPHERALS && + num != TYPE_CMD) { + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, + "Marking buffer as free after write done p: %d, t: %d, buf_num: %d\n", + peripheral, type, num); diagfwd_write_done(peripheral, type, num); - } - if (peripheral == APPS_DATA || - ctxt == DIAG_MEMORY_DEVICE_MODE) { + } else if (peripheral == APPS_DATA || + (peripheral >= 0 && peripheral < NUM_PERIPHERALS && + num == TYPE_CMD)) { + DIAG_LOG(DIAG_DEBUG_PERIPHERALS, + "Marking APPS response buffer free after write done for p: %d, t: %d, buf_num: %d\n", + peripheral, type, num); spin_lock_irqsave(&driver->rsp_buf_busy_lock, flags); driver->rsp_buf_busy = 0; driver->encoded_rsp_len = 0; @@ -1703,6 +1738,8 @@ int diagfwd_init(void) , GFP_KERNEL)) == NULL) goto err; kmemleak_not_leak(driver->data_ready); + for (i = 0; i < THRESHOLD_CLIENT_LIMIT; i++) + atomic_set(&driver->data_ready_notif[i], 0); if (driver->apps_req_buf == NULL) { driver->apps_req_buf = kzalloc(DIAG_MAX_REQ_SIZE, GFP_KERNEL); if (!driver->apps_req_buf) diff --git a/drivers/char/diag/diagfwd.h b/drivers/char/diag/diagfwd.h index 97ad3f60ba5e..8b097cfc4527 100644 --- a/drivers/char/diag/diagfwd.h +++ b/drivers/char/diag/diagfwd.h @@ -30,10 +30,8 @@ int diagfwd_init(void); void diagfwd_exit(void); -void diag_process_hdlc_pkt(void *data, unsigned len, - struct diag_md_session_t *info); -void diag_process_non_hdlc_pkt(unsigned char *data, int len, - struct diag_md_session_t *info); +void diag_process_hdlc_pkt(void *data, unsigned int len, int pid); +void diag_process_non_hdlc_pkt(unsigned char *data, int len, int pid); int chk_config_get_id(void); int chk_apps_only(void); int chk_apps_master(void); @@ -45,10 +43,8 @@ int diag_cmd_get_mobile_id(unsigned char *src_buf, int src_len, int diag_check_common_cmd(struct diag_pkt_header_t *header); void diag_update_userspace_clients(unsigned int type); void diag_update_sleeping_process(int process_id, int data_type); -int diag_process_apps_pkt(unsigned char *buf, int len, - struct diag_md_session_t *info); -void diag_send_error_rsp(unsigned char *buf, int len, - struct diag_md_session_t *info); +int diag_process_apps_pkt(unsigned char *buf, int len, int pid); +void diag_send_error_rsp(unsigned char *buf, int len, int pid); void diag_update_pkt_buffer(unsigned char *buf, uint32_t len, int type); int diag_process_stm_cmd(unsigned char *buf, unsigned char *dest_buf); void diag_md_hdlc_reset_timer_func(unsigned long pid); diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c index 10038e629e6c..78088ab33439 100644 --- a/drivers/char/diag/diagfwd_cntl.c +++ b/drivers/char/diag/diagfwd_cntl.c @@ -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 @@ -443,8 +443,8 @@ static void process_last_event_report(uint8_t *buf, uint32_t len, header = (struct diag_ctrl_last_event_report *)ptr; event_size = ((header->event_last_id / 8) + 1); if (event_size >= driver->event_mask_size) { - pr_debug("diag: In %s, receiving event mask size more that Apps can handle\n", - __func__); + DIAG_LOG(DIAG_DEBUG_MASKS, + "diag: receiving event mask size more that Apps can handle\n"); temp = krealloc(driver->event_mask->ptr, event_size, GFP_KERNEL); if (!temp) { @@ -558,6 +558,10 @@ static void process_ssid_range_report(uint8_t *buf, uint32_t len, mask_ptr = (struct diag_msg_mask_t *)msg_mask.ptr; found = 0; for (j = 0; j < driver->msg_mask_tbl_count; j++, mask_ptr++) { + if (!mask_ptr || !ssid_range) { + found = 1; + break; + } if (mask_ptr->ssid_first != ssid_range->ssid_first) continue; mutex_lock(&mask_ptr->lock); @@ -576,6 +580,8 @@ static void process_ssid_range_report(uint8_t *buf, uint32_t len, new_size = (driver->msg_mask_tbl_count + 1) * sizeof(struct diag_msg_mask_t); + DIAG_LOG(DIAG_DEBUG_MASKS, + "diag: receiving msg mask size more that Apps can handle\n"); temp = krealloc(msg_mask.ptr, new_size, GFP_KERNEL); if (!temp) { pr_err("diag: In %s, Unable to add new ssid table to msg mask, ssid first: %d, last: %d\n", @@ -584,6 +590,7 @@ static void process_ssid_range_report(uint8_t *buf, uint32_t len, continue; } msg_mask.ptr = temp; + mask_ptr = (struct diag_msg_mask_t *)msg_mask.ptr; err = diag_create_msg_mask_table_entry(mask_ptr, ssid_range); if (err) { pr_err("diag: In %s, Unable to create a new msg mask table entry, first: %d last: %d err: %d\n", @@ -623,6 +630,10 @@ static void diag_build_time_mask_update(uint8_t *buf, num_items = range->ssid_last - range->ssid_first + 1; for (i = 0; i < driver->bt_msg_mask_tbl_count; i++, build_mask++) { + if (!build_mask) { + found = 1; + break; + } if (build_mask->ssid_first != range->ssid_first) continue; found = 1; @@ -633,7 +644,8 @@ static void diag_build_time_mask_update(uint8_t *buf, __func__); } dest_ptr = build_mask->ptr; - for (j = 0; j < build_mask->range; j++, mask_ptr++, dest_ptr++) + for (j = 0; (j < build_mask->range) && mask_ptr && dest_ptr; + j++, mask_ptr++, dest_ptr++) *(uint32_t *)dest_ptr |= *mask_ptr; mutex_unlock(&build_mask->lock); break; @@ -641,8 +653,12 @@ static void diag_build_time_mask_update(uint8_t *buf, if (found) goto end; + new_size = (driver->bt_msg_mask_tbl_count + 1) * sizeof(struct diag_msg_mask_t); + DIAG_LOG(DIAG_DEBUG_MASKS, + "diag: receiving build time mask size more that Apps can handle\n"); + temp = krealloc(driver->build_time_mask->ptr, new_size, GFP_KERNEL); if (!temp) { pr_err("diag: In %s, unable to create a new entry for build time mask\n", @@ -650,6 +666,7 @@ static void diag_build_time_mask_update(uint8_t *buf, goto end; } driver->build_time_mask->ptr = temp; + build_mask = (struct diag_msg_mask_t *)driver->build_time_mask->ptr; err = diag_create_msg_mask_table_entry(build_mask, range); if (err) { pr_err("diag: In %s, Unable to create a new msg mask table entry, err: %d\n", diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c index 7e428ce972a8..a7abe3dafb69 100644 --- a/drivers/char/diag/diagfwd_peripheral.c +++ b/drivers/char/diag/diagfwd_peripheral.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 @@ -316,14 +316,13 @@ static void diagfwd_data_process_done(struct diagfwd_info *fwd_info, diag_ws_release(); return; } - - session_info = - diag_md_session_get_peripheral(peripheral); + mutex_lock(&driver->md_session_lock); + session_info = diag_md_session_get_peripheral(peripheral); if (session_info) hdlc_disabled = session_info->hdlc_disabled; else hdlc_disabled = driver->hdlc_disabled; - + mutex_unlock(&driver->md_session_lock); if (hdlc_disabled) { /* The data is raw and and on APPS side HDLC is disabled */ if (!buf) { @@ -638,12 +637,13 @@ static void diagfwd_data_read_done(struct diagfwd_info *fwd_info, mutex_lock(&driver->hdlc_disable_mutex); mutex_lock(&fwd_info->data_mutex); + mutex_lock(&driver->md_session_lock); session_info = diag_md_session_get_peripheral(fwd_info->peripheral); if (session_info) hdlc_disabled = session_info->hdlc_disabled; else hdlc_disabled = driver->hdlc_disabled; - + mutex_unlock(&driver->md_session_lock); if (!driver->feature[fwd_info->peripheral].encode_hdlc) { if (fwd_info->buf_1 && fwd_info->buf_1->data == buf) { temp_buf = fwd_info->buf_1; diff --git a/drivers/clk/msm/clock-osm.c b/drivers/clk/msm/clock-osm.c index eb72217b9b1c..a1635bad3bb0 100644 --- a/drivers/clk/msm/clock-osm.c +++ b/drivers/clk/msm/clock-osm.c @@ -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 @@ -2868,7 +2868,7 @@ static ssize_t debugfs_trace_method_get(struct file *file, char __user *buf, else if (c->trace_method == XOR_PACKET) len = snprintf(debug_buf, sizeof(debug_buf), "xor\n"); - rc = simple_read_from_buffer((void __user *) buf, len, ppos, + rc = simple_read_from_buffer((void __user *) buf, count, ppos, (void *) debug_buf, len); mutex_unlock(&debug_buf_mutex); diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c index 510a9803bd82..68f4afde0d0c 100644 --- a/drivers/clk/qcom/clk-cpu-osm.c +++ b/drivers/clk/qcom/clk-cpu-osm.c @@ -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 @@ -2691,7 +2691,7 @@ static ssize_t debugfs_trace_method_get(struct file *file, char __user *buf, else if (c->trace_method == XOR_PACKET) len = snprintf(debug_buf, sizeof(debug_buf), "xor\n"); - rc = simple_read_from_buffer((void __user *) buf, len, ppos, + rc = simple_read_from_buffer((void __user *) buf, count, ppos, (void *) debug_buf, len); mutex_unlock(&debug_buf_mutex); diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index ff0c8327fabe..3c3cf8e04eea 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2013, 2016-2018, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -210,9 +210,11 @@ static unsigned long clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_rcg2 *rcg = to_clk_rcg2(hw); + const struct freq_tbl *f_curr; u32 cfg, hid_div, m = 0, n = 0, mode = 0, mask; - if (rcg->enable_safe_config && !clk_hw_is_prepared(hw)) { + if (rcg->enable_safe_config && (!clk_hw_is_prepared(hw) + || !clk_hw_is_enabled(hw))) { if (!rcg->current_freq) rcg->current_freq = cxo_f.freq; return rcg->current_freq; @@ -232,9 +234,17 @@ clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) mode >>= CFG_MODE_SHIFT; } - mask = BIT(rcg->hid_width) - 1; - hid_div = cfg >> CFG_SRC_DIV_SHIFT; - hid_div &= mask; + if (rcg->enable_safe_config) { + f_curr = qcom_find_freq(rcg->freq_tbl, rcg->current_freq); + if (!f_curr) + return -EINVAL; + + hid_div = f_curr->pre_div; + } else { + mask = BIT(rcg->hid_width) - 1; + hid_div = cfg >> CFG_SRC_DIV_SHIFT; + hid_div &= mask; + } return calc_rate(parent_rate, m, n, mode, hid_div); } diff --git a/drivers/clk/qcom/gpucc-sdm660.c b/drivers/clk/qcom/gpucc-sdm660.c index 8b2e6fd601c0..ff837aad0c9a 100644 --- a/drivers/clk/qcom/gpucc-sdm660.c +++ b/drivers/clk/qcom/gpucc-sdm660.c @@ -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 @@ -181,6 +181,7 @@ static const struct freq_tbl ftbl_gfx3d_clk_src[] = { F_GFX(370000000, 0, 2, 0, 0, 740000000), F_GFX(430000000, 0, 2, 0, 0, 860000000), F_GFX(465000000, 0, 2, 0, 0, 930000000), + F_GFX(585000000, 0, 2, 0, 0, 1170000000), F_GFX(588000000, 0, 2, 0, 0, 1176000000), F_GFX(647000000, 0, 2, 0, 0, 1294000000), F_GFX(700000000, 0, 2, 0, 0, 1400000000), diff --git a/drivers/clk/qcom/mmcc-sdm660.c b/drivers/clk/qcom/mmcc-sdm660.c index 542737e4d204..05606f1b23dc 100644 --- a/drivers/clk/qcom/mmcc-sdm660.c +++ b/drivers/clk/qcom/mmcc-sdm660.c @@ -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 @@ -2419,7 +2419,7 @@ static struct clk_regmap_div mmss_mdss_byte0_intf_div_clk = { }, .num_parents = 1, .ops = &clk_regmap_div_ops, - .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .flags = CLK_GET_RATE_NOCACHE, }, }, }; @@ -2476,7 +2476,7 @@ static struct clk_regmap_div mmss_mdss_byte1_intf_div_clk = { }, .num_parents = 1, .ops = &clk_regmap_div_ops, - .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .flags = CLK_GET_RATE_NOCACHE, }, }, }; diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c index 59239f871453..b4675df551b3 100644 --- a/drivers/cpuidle/lpm-levels-of.c +++ b/drivers/cpuidle/lpm-levels-of.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 @@ -451,10 +451,6 @@ static int parse_legacy_cluster_params(struct device_node *node, return 0; failed: pr_err("%s(): Failed reading %s\n", __func__, key); - kfree(c->name); - kfree(c->lpm_dev); - c->name = NULL; - c->lpm_dev = NULL; return ret; } @@ -640,8 +636,6 @@ static int parse_cluster_level(struct device_node *node, return 0; failed: pr_err("Failed %s() key = %s ret = %d\n", __func__, key, ret); - kfree(level->mode); - level->mode = NULL; return ret; } @@ -836,19 +830,12 @@ static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c) return 0; failed: - for (i = 0; i < c->cpu->nlevels; i++) { - kfree(c->cpu->levels[i].name); - c->cpu->levels[i].name = NULL; - } - kfree(c->cpu); - c->cpu = NULL; pr_err("%s(): Failed with error code:%d\n", __func__, ret); return ret; } void free_cluster_node(struct lpm_cluster *cluster) { - int i; struct lpm_cluster *cl, *m; list_for_each_entry_safe(cl, m, &cluster->child, list) { @@ -856,22 +843,6 @@ void free_cluster_node(struct lpm_cluster *cluster) free_cluster_node(cl); }; - if (cluster->cpu) { - for (i = 0; i < cluster->cpu->nlevels; i++) { - kfree(cluster->cpu->levels[i].name); - cluster->cpu->levels[i].name = NULL; - } - } - for (i = 0; i < cluster->nlevels; i++) { - kfree(cluster->levels[i].mode); - cluster->levels[i].mode = NULL; - } - kfree(cluster->cpu); - kfree(cluster->name); - kfree(cluster->lpm_dev); - cluster->cpu = NULL; - cluster->name = NULL; - cluster->lpm_dev = NULL; cluster->ndevices = 0; } @@ -989,9 +960,7 @@ struct lpm_cluster *parse_cluster(struct device_node *node, list_del(&c->list); free_cluster_node(c); failed_parse_params: - c->parent = NULL; pr_err("Failed parse params\n"); - kfree(c); return NULL; } struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev) diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h index ebde695e99c7..c4769c3b336d 100644 --- a/drivers/devfreq/governor.h +++ b/drivers/devfreq/governor.h @@ -4,6 +4,8 @@ * Copyright (C) 2011 Samsung Electronics * MyungJoo Ham * + * 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 as * published by the Free Software Foundation. @@ -25,9 +27,6 @@ #define DEVFREQ_GOV_SUSPEND 0x4 #define DEVFREQ_GOV_RESUME 0x5 -/* Caution: devfreq->lock must be locked before calling update_devfreq */ -extern int update_devfreq(struct devfreq *devfreq); - extern void devfreq_monitor_start(struct devfreq *devfreq); extern void devfreq_monitor_stop(struct devfreq *devfreq); extern void devfreq_monitor_suspend(struct devfreq *devfreq); diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c index d652f3b53635..497143f01c72 100644 --- a/drivers/firmware/psci.c +++ b/drivers/firmware/psci.c @@ -431,6 +431,8 @@ static void __init psci_init_migrate(void) static void __init psci_0_2_set_functions(void) { pr_info("Using standard PSCI v0.2 function IDs\n"); + psci_ops.get_version = psci_get_version; + psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_FN_NATIVE(0_2, CPU_SUSPEND); psci_ops.cpu_suspend = psci_cpu_suspend; diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 47c1747e7ae3..6bf4588de46c 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1002,7 +1002,7 @@ static const struct drm_display_mode edid_cea_modes[] = { .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 64 - 1920x1080@100Hz */ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448, - 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, + 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 65 - 1280x720@24Hz */ @@ -1067,159 +1067,159 @@ static const struct drm_display_mode edid_cea_modes[] = { .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 77 - 1920x1080@100Hz */ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448, - 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, + 2492, 2640, 0, 1080, 1084, 1089, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 78 - 1920x1080@120Hz */ { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008, 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 79 - 1680x720@24Hz */ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 3040, - 3080, 3300, 0, 720, 725, 730, 750, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + 3080, 3300, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 80 - 1680x720@25Hz */ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2908, - 2948, 3168, 0, 720, 725, 730, 750, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + 2948, 3168, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 81 - 1680x720@30Hz */ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 59400, 1680, 2380, - 2420, 2640, 0, 720, 725, 730, 750, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + 2420, 2640, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 82 - 1680x720@50Hz */ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 82500, 1680, 1940, - 1980, 2200, 0, 720, 725, 730, 750, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + 1980, 2200, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 83 - 1680x720@60Hz */ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 99000, 1680, 1940, - 1980, 2200, 0, 720, 725, 730, 750, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + 1980, 2200, 0, 720, 725, 730, 750, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 84 - 1680x720@100Hz */ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 165000, 1680, 1740, - 1780, 2000, 0, 720, 725, 730, 825, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + 1780, 2000, 0, 720, 725, 730, 825, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 85 - 1680x720@120Hz */ { DRM_MODE("1680x720", DRM_MODE_TYPE_DRIVER, 198000, 1680, 1740, - 1780, 2000, 0, 720, 725, 730, 825, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + 1780, 2000, 0, 720, 725, 730, 825, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 86 - 2560x1080@24Hz */ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 99000, 2560, 3558, - 3602, 3750, 0, 1080, 1084, 1089, 1100, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + 3602, 3750, 0, 1080, 1084, 1089, 1100, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 87 - 2560x1080@25Hz */ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 90000, 2560, 3008, - 3052, 3200, 0, 1080, 1084, 1089, 1125, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + 3052, 3200, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 88 - 2560x1080@30Hz */ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 118800, 2560, 3328, - 3372, 3520, 0, 1080, 1084, 1089, 1125, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + 3372, 3520, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 89 - 2560x1080@50Hz */ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 185625, 2560, 3108, - 3152, 3300, 0, 1080, 1084, 1089, 1125, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + 3152, 3300, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 90 - 2560x1080@60Hz */ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 198000, 2560, 2808, - 2852, 3000, 0, 1080, 1084, 1089, 1100, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + 2852, 3000, 0, 1080, 1084, 1089, 1100, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 91 - 2560x1080@100Hz */ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 371250, 2560, 2778, - 2822, 2970, 0, 1080, 1084, 1089, 1250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + 2822, 2970, 0, 1080, 1084, 1089, 1250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 92 - 2560x1080@120Hz */ { DRM_MODE("2560x1080", DRM_MODE_TYPE_DRIVER, 495000, 2560, 3108, - 3152, 3300, 0, 1080, 1084, 1089, 1250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, + 3152, 3300, 0, 1080, 1084, 1089, 1250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 93 - 3840x2160p@24Hz 16:9 */ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116, - 5204, 5500, 0, 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9,}, + 5204, 5500, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 94 - 3840x2160p@25Hz 16:9 */ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4896, - 4984, 5280, 0, 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9}, + 4984, 5280, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 95 - 3840x2160p@30Hz 16:9 */ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016, - 4104, 4400, 0, 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9}, + 4104, 4400, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 96 - 3840x2160p@50Hz 16:9 */ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896, - 4984, 5280, 0, 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9}, + 4984, 5280, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 97 - 3840x2160p@60Hz 16:9 */ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016, - 4104, 4400, 0, 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9}, + 4104, 4400, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, }, /* 98 - 4096x2160p@24Hz 256:135 */ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5116, - 5204, 5500, 0, 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135}, + 5204, 5500, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, }, /* 99 - 4096x2160p@25Hz 256:135 */ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 5064, - 5152, 5280, 0, 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135}, + 5152, 5280, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, }, /* 100 - 4096x2160p@30Hz 256:135 */ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 297000, 4096, 4184, - 4272, 4400, 0, 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135}, + 4272, 4400, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, }, /* 101 - 4096x2160p@50Hz 256:135 */ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 5064, - 5152, 5280, 0, 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135}, + 5152, 5280, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, }, /* 102 - 4096x2160p@60Hz 256:135 */ { DRM_MODE("4096x2160", DRM_MODE_TYPE_DRIVER, 594000, 4096, 4184, - 4272, 4400, 0, 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135}, + 4272, 4400, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135, }, /* 103 - 3840x2160p@24Hz 64:27 */ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 5116, - 5204, 5500, 0, 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27}, + 5204, 5500, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 24, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 104 - 3840x2160p@25Hz 64:27 */ - { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016, - 4104, 4400, 0, 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27}, + { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4896, + 4984, 5280, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 25, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 105 - 3840x2160p@30Hz 64:27 */ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 297000, 3840, 4016, - 4104, 4400, 0, 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27}, + 4104, 4400, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 30, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 106 - 3840x2160p@50Hz 64:27 */ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4896, - 4984, 5280, 0, 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27}, + 4984, 5280, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 50, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, /* 107 - 3840x2160p@60Hz 64:27 */ { DRM_MODE("3840x2160", DRM_MODE_TYPE_DRIVER, 594000, 3840, 4016, - 4104, 4400, 0, 2160, 2168, 2178, 2250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), - .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27}, + 4104, 4400, 0, 2160, 2168, 2178, 2250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27, }, }; /* diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index 6e4dd62d4ed9..dbf263d3511b 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -541,6 +541,9 @@ static struct drm_mm_node *get_first_hole(const struct drm_mm *mm, if (flags & DRM_MM_SEARCH_BOTTOM_UP) { struct rb_node *node = rb_first(&mm->holes_tree); + if (!node) + return NULL; + return rb_entry(node, struct drm_mm_node, hole_node); } else if (flags & DRM_MM_SEARCH_BELOW) { return list_entry((mm)->hole_stack.prev, @@ -555,8 +558,12 @@ static struct drm_mm_node *get_next_hole(struct drm_mm_node *entry, enum drm_mm_search_flags flags) { if (flags & DRM_MM_SEARCH_BOTTOM_UP) { - return rb_entry(rb_next(&entry->hole_node), - struct drm_mm_node, hole_node); + struct rb_node *node = rb_next(&entry->hole_node); + + if (!node) + return NULL; + + return rb_entry(node, struct drm_mm_node, hole_node); } else if (flags & DRM_MM_SEARCH_BELOW) { return list_entry(entry->hole_stack.prev, struct drm_mm_node, hole_stack); diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 71a10f08522e..c2f23a344b62 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -949,6 +949,7 @@ bool drm_mode_equal_no_clocks_no_stereo(const struct drm_display_mode *mode1, mode1->vsync_end == mode2->vsync_end && mode1->vtotal == mode2->vtotal && mode1->vscan == mode2->vscan && + mode1->picture_aspect_ratio == mode2->picture_aspect_ratio && (mode1->flags & ~DRM_MODE_FLAG_3D_MASK) == (mode2->flags & ~DRM_MODE_FLAG_3D_MASK)) return true; @@ -1445,6 +1446,27 @@ void drm_mode_convert_to_umode(struct drm_mode_modeinfo *out, out->vrefresh = in->vrefresh; out->flags = in->flags; out->type = in->type; + out->flags &= ~DRM_MODE_FLAG_PIC_AR_MASK; + + switch (in->picture_aspect_ratio) { + case HDMI_PICTURE_ASPECT_4_3: + out->flags |= DRM_MODE_FLAG_PIC_AR_4_3; + break; + case HDMI_PICTURE_ASPECT_16_9: + out->flags |= DRM_MODE_FLAG_PIC_AR_16_9; + break; + case HDMI_PICTURE_ASPECT_64_27: + out->flags |= DRM_MODE_FLAG_PIC_AR_64_27; + break; + case DRM_MODE_PICTURE_ASPECT_256_135: + out->flags |= DRM_MODE_FLAG_PIC_AR_256_135; + break; + case HDMI_PICTURE_ASPECT_RESERVED: + default: + out->flags |= DRM_MODE_FLAG_PIC_AR_NONE; + break; + } + strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); out->name[DRM_DISPLAY_MODE_LEN-1] = 0; } @@ -1490,6 +1512,27 @@ int drm_mode_convert_umode(struct drm_display_mode *out, strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); out->name[DRM_DISPLAY_MODE_LEN-1] = 0; + /* Clearing picture aspect ratio bits from out flags */ + out->flags &= ~DRM_MODE_FLAG_PIC_AR_MASK; + + switch (in->flags & DRM_MODE_FLAG_PIC_AR_MASK) { + case DRM_MODE_FLAG_PIC_AR_4_3: + out->picture_aspect_ratio |= HDMI_PICTURE_ASPECT_4_3; + break; + case DRM_MODE_FLAG_PIC_AR_16_9: + out->picture_aspect_ratio |= HDMI_PICTURE_ASPECT_16_9; + break; + case DRM_MODE_FLAG_PIC_AR_64_27: + out->picture_aspect_ratio |= HDMI_PICTURE_ASPECT_64_27; + break; + case DRM_MODE_FLAG_PIC_AR_256_135: + out->picture_aspect_ratio |= HDMI_PICTURE_ASPECT_256_135; + break; + default: + out->picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE; + break; + } + out->status = drm_mode_validate_basic(out); if (out->status != MODE_OK) goto out; diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index dbc198b00792..cb3b25ddd0da 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -98,3 +98,13 @@ config DRM_SDE_HDMI default y help Choose this option if HDMI connector support is needed in SDE driver. + +config DRM_SDE_EVTLOG_DEBUG + bool "Enable event logging in MSM DRM" + depends on DRM_MSM + help + The SDE DRM debugging provides support to enable display debugging + features to: dump SDE registers during driver errors, panic + driver during fatal errors and enable some display-driver logging + into an internal buffer (this avoids logging overhead). + diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 4c082fff2fc5..678b2178cb69 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -49,6 +49,7 @@ msm_drm-y := \ sde/sde_color_processing.o \ sde/sde_vbif.o \ sde/sde_splash.o \ + sde_dbg.o \ sde_dbg_evtlog.o \ sde_io_util.o \ dba_bridge.o \ diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index c085e173232b..fc9e471843b0 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -2,7 +2,7 @@ * Copyright (C) 2013 Red Hat * Author: Rob Clark * - * Copyright (c) 2014 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 as published by @@ -483,8 +483,6 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev) adreno_gpu = &a3xx_gpu->base; gpu = &adreno_gpu->base; - a3xx_gpu->pdev = pdev; - gpu->perfcntrs = perfcntrs; gpu->num_perfcntrs = ARRAY_SIZE(perfcntrs); diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.h b/drivers/gpu/drm/msm/adreno/a3xx_gpu.h index 85ff66cbddd6..1b0d3b47c371 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.h @@ -2,6 +2,8 @@ * Copyright (C) 2013 Red Hat * Author: Rob Clark * + * 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 as published by * the Free Software Foundation. @@ -28,7 +30,6 @@ struct a3xx_gpu { struct adreno_gpu base; - struct platform_device *pdev; /* if OCMEM is used for GMEM: */ uint32_t ocmem_base; diff --git a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c index 624c2a87d593..fc1f06592745 100644 --- a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014 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 @@ -560,8 +560,6 @@ struct msm_gpu *a4xx_gpu_init(struct drm_device *dev) adreno_gpu = &a4xx_gpu->base; gpu = &adreno_gpu->base; - a4xx_gpu->pdev = pdev; - gpu->perfcntrs = NULL; gpu->num_perfcntrs = 0; diff --git a/drivers/gpu/drm/msm/adreno/a4xx_gpu.h b/drivers/gpu/drm/msm/adreno/a4xx_gpu.h index 01247204ac92..414530d134b4 100644 --- a/drivers/gpu/drm/msm/adreno/a4xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a4xx_gpu.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014 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 @@ -23,7 +23,6 @@ struct a4xx_gpu { struct adreno_gpu base; - struct platform_device *pdev; /* if OCMEM is used for GMEM: */ uint32_t ocmem_base; diff --git a/drivers/gpu/drm/msm/adreno/a5xx_counters.c b/drivers/gpu/drm/msm/adreno/a5xx_counters.c index bc442039c308..497f929375af 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_counters.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_counters.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 @@ -225,7 +225,7 @@ static void a5xx_counter_enable_alwayson_power(struct msm_gpu *gpu, static u64 a5xx_counter_read(struct msm_gpu *gpu, struct adreno_counter_group *group, int counterid) { - if (counterid >= group->nr_counters) + if (counterid < 0 || counterid >= group->nr_counters) return 0; return gpu_read64(gpu, group->counters[counterid].lo, diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 765c1c087c76..3ebc4f24bb4b 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.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 @@ -784,6 +784,10 @@ static int a5xx_hw_init(struct msm_gpu *gpu) a5xx_gpu->timestamp_counter = adreno_get_counter(gpu, MSM_COUNTER_GROUP_CP, 0, NULL, NULL); + /* Get RBBM performance counter countable 6 to read GPU busy cycles */ + a5xx_gpu->gpu_busy_counter = adreno_get_counter(gpu, + MSM_COUNTER_GROUP_RBBM, 6, NULL, NULL); + /* Load the GPMU firmware before starting the HW init */ a5xx_gpmu_ucode_init(gpu); @@ -1300,6 +1304,15 @@ static struct msm_ringbuffer *a5xx_active_ring(struct msm_gpu *gpu) return a5xx_gpu->cur_ring; } +static u64 a5xx_gpu_busy(struct msm_gpu *gpu) +{ + struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu); + + return adreno_read_counter(gpu, MSM_COUNTER_GROUP_RBBM, + a5xx_gpu->gpu_busy_counter); +} + static const struct adreno_gpu_funcs funcs = { .base = { .get_param = adreno_get_param, @@ -1320,6 +1333,7 @@ static const struct adreno_gpu_funcs funcs = { .get_counter = adreno_get_counter, .read_counter = adreno_read_counter, .put_counter = adreno_put_counter, + .gpu_busy = a5xx_gpu_busy, }, .get_timestamp = a5xx_get_timestamp, }; @@ -1426,7 +1440,6 @@ struct msm_gpu *a5xx_gpu_init(struct drm_device *dev) adreno_gpu = &a5xx_gpu->base; gpu = &adreno_gpu->base; - a5xx_gpu->pdev = pdev; adreno_gpu->registers = a5xx_registers; adreno_gpu->reg_offsets = a5xx_register_offsets; diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h index c30b65785ab6..3f261de2852d 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.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 @@ -30,7 +30,6 @@ struct a5xx_gpu { unsigned long flags; struct adreno_gpu base; - struct platform_device *pdev; struct drm_gem_object *pm4_bo; uint64_t pm4_iova; @@ -59,6 +58,7 @@ struct a5xx_gpu { uint64_t smmu_info_iova; int timestamp_counter; + int gpu_busy_counter; }; #define to_a5xx_gpu(x) container_of(x, struct a5xx_gpu, base) diff --git a/drivers/gpu/drm/msm/adreno/a5xx_power.c b/drivers/gpu/drm/msm/adreno/a5xx_power.c index 647b61313fc2..f3ea248851db 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_power.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_power.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 @@ -130,7 +130,7 @@ static void a540_lm_setup(struct msm_gpu *gpu) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); u32 max_power = 0; - u32 rate = gpu->gpufreq[gpu->active_level]; + u32 rate = gpu->gpufreq[0]; u32 config; /* The battery current limiter isn't enabled for A540 */ @@ -146,10 +146,6 @@ static void a540_lm_setup(struct msm_gpu *gpu) gpu_write(gpu, AGC_MSG_STATE, 0x80000001); gpu_write(gpu, AGC_MSG_COMMAND, AGC_POWER_CONFIG_PRODUCTION_ID); - /* - * For now just write the one voltage level - we will do more when we - * can do scaling - */ gpu_write(gpu, AGC_MSG_PAYLOAD(0), max_power); gpu_write(gpu, AGC_MSG_PAYLOAD(1), 1); @@ -169,7 +165,7 @@ static void a530_lm_setup(struct msm_gpu *gpu) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu); - uint32_t rate = gpu->gpufreq[gpu->active_level]; + uint32_t rate = gpu->gpufreq[0]; uint32_t tsens = 0; uint32_t max_power = 0; unsigned int i; @@ -201,10 +197,6 @@ static void a530_lm_setup(struct msm_gpu *gpu) gpu_write(gpu, AGC_MSG_STATE, 1); gpu_write(gpu, AGC_MSG_COMMAND, AGC_POWER_CONFIG_PRODUCTION_ID); - /* - * For now just write the one voltage level - we will do more when we - * can do scaling - */ gpu_write(gpu, AGC_MSG_PAYLOAD(0), max_power); gpu_write(gpu, AGC_MSG_PAYLOAD(1), 1); diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 04e0056f2a49..21fad68f2126 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -2,7 +2,7 @@ * Copyright (C) 2013 Red Hat * Author: Rob Clark * - * Copyright (c) 2014,2016-2017 The Linux Foundation. All rights reserved. + * Copyright (c) 2014,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 as published by @@ -45,7 +45,7 @@ int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value) (adreno_gpu->rev.core << 24); return 0; case MSM_PARAM_MAX_FREQ: - *value = gpu->gpufreq[gpu->active_level]; + *value = gpu->gpufreq[0]; return 0; case MSM_PARAM_TIMESTAMP: if (adreno_gpu->funcs->get_timestamp) @@ -389,14 +389,6 @@ static int _adreno_get_pwrlevels(struct msm_gpu *gpu, struct device_node *node) { struct device_node *child; - gpu->active_level = 1; - - /* The device tree will tell us the best clock to initialize with */ - of_property_read_u32(node, "qcom,initial-pwrlevel", &gpu->active_level); - - if (gpu->active_level >= ARRAY_SIZE(gpu->gpufreq)) - gpu->active_level = 1; - for_each_child_of_node(node, child) { unsigned int index; @@ -415,9 +407,9 @@ static int _adreno_get_pwrlevels(struct msm_gpu *gpu, struct device_node *node) } DBG("fast_rate=%u, slow_rate=%u, bus_freq=%u", - gpu->gpufreq[gpu->active_level], + gpu->gpufreq[0], gpu->gpufreq[gpu->nr_pwrlevels - 1], - gpu->busfreq[gpu->active_level]); + gpu->busfreq[0]); return 0; } diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c index fb9617ce572a..ee6470b93055 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c @@ -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 @@ -484,7 +484,7 @@ static int dsi_ctrl_init_regmap(struct platform_device *pdev, } ctrl->hw.base = ptr; - pr_debug("[%s] map dsi_ctrl registers to %p\n", ctrl->name, + pr_debug("[%s] map dsi_ctrl registers to %pK\n", ctrl->name, ctrl->hw.base); ptr = msm_ioremap(pdev, "mmss_misc", ctrl->name); diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c index 1ccbbe7df573..f7a77336e691 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 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 @@ -93,7 +93,8 @@ static int dsi_phy_regmap_init(struct platform_device *pdev, phy->hw.base = ptr; - pr_debug("[%s] map dsi_phy registers to %p\n", phy->name, phy->hw.base); + pr_debug("[%s] map dsi_phy registers to %pK\n", + phy->name, phy->hw.base); return rc; } diff --git a/drivers/gpu/drm/msm/edp/edp.c b/drivers/gpu/drm/msm/edp/edp.c index 0940e84b2821..2c9d11638f29 100644 --- a/drivers/gpu/drm/msm/edp/edp.c +++ b/drivers/gpu/drm/msm/edp/edp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-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 @@ -54,7 +54,7 @@ static struct msm_edp *edp_init(struct platform_device *pdev) ret = -ENOMEM; goto fail; } - DBG("eDP probed=%p", edp); + DBG("eDP probed=%pK", edp); edp->pdev = pdev; platform_set_drvdata(pdev, edp); diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index 0f77e35ef287..01157a0db257 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -1215,6 +1215,9 @@ static int _sde_hdmi_gpio_config(struct hdmi *hdmi, bool on) gpio_free(config->hpd_gpio); + if (config->hpd5v_gpio != -1) + gpio_free(config->hpd5v_gpio); + if (config->mux_en_gpio != -1) { gpio_set_value_cansleep(config->mux_en_gpio, 0); gpio_free(config->mux_en_gpio); @@ -1336,19 +1339,26 @@ static int _sde_hdmi_hpd_enable(struct sde_hdmi *sde_hdmi) HDMI_HPD_CTRL_ENABLE | hpd_ctrl); spin_unlock_irqrestore(&hdmi->reg_lock, flags); + hdmi->hpd_off = false; + SDE_DEBUG("enabled hdmi hpd\n"); return 0; fail: return ret; } -static void _sde_hdmi_hdp_disable(struct sde_hdmi *sde_hdmi) +static void _sde_hdmi_hpd_disable(struct sde_hdmi *sde_hdmi) { struct hdmi *hdmi = sde_hdmi->ctrl.ctrl; const struct hdmi_platform_config *config = hdmi->config; struct device *dev = &hdmi->pdev->dev; int i, ret = 0; + if (hdmi->hpd_off) { + pr_warn("hdmi display hpd was already disabled\n"); + return; + } + /* Disable HPD interrupt */ hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, 0); @@ -1371,6 +1381,36 @@ static void _sde_hdmi_hdp_disable(struct sde_hdmi *sde_hdmi) pr_warn("failed to disable hpd regulator: %s (%d)\n", config->hpd_reg_names[i], ret); } + hdmi->hpd_off = true; + SDE_DEBUG("disabled hdmi hpd\n"); +} + +/** + * _sde_hdmi_update_hpd_state() - Update the HDMI HPD clock state + * + * @state: non-zero to disbale HPD clock, 0 to enable. + * return: 0 on success, non-zero in case of failure. + * + */ +static int +_sde_hdmi_update_hpd_state(struct sde_hdmi *hdmi_display, u64 state) +{ + struct hdmi *hdmi = hdmi_display->ctrl.ctrl; + int rc = 0; + + if (hdmi_display->non_pluggable) + return 0; + + SDE_DEBUG("changing hdmi hpd state to %llu\n", state); + + if (state == SDE_MODE_HPD_ON) { + if (!hdmi->hpd_off) + pr_warn("hdmi display hpd was already enabled\n"); + rc = _sde_hdmi_hpd_enable(hdmi_display); + } else + _sde_hdmi_hpd_disable(hdmi_display); + + return rc; } static void _sde_hdmi_cec_update_phys_addr(struct sde_hdmi *display) @@ -2140,6 +2180,8 @@ int sde_hdmi_set_property(struct drm_connector *connector, rc = _sde_hdmi_enable_pll_update(display, value); else if (property_index == CONNECTOR_PROP_PLL_DELTA) rc = _sde_hdmi_update_pll_delta(display, value); + else if (property_index == CONNECTOR_PROP_HPD_OFF) + rc = _sde_hdmi_update_hpd_state(display, value); return rc; } @@ -2217,7 +2259,7 @@ int sde_hdmi_connector_pre_deinit(struct drm_connector *connector, return -EINVAL; } - _sde_hdmi_hdp_disable(sde_hdmi); + _sde_hdmi_hpd_disable(sde_hdmi); return 0; } @@ -2630,6 +2672,13 @@ enum drm_mode_status sde_hdmi_mode_valid(struct drm_connector *connector, if (actual != requested) return MODE_CLOCK_RANGE; + /* if no format flags are present remove the mode */ + if (!(mode->flags & SDE_DRM_MODE_FLAG_FMT_MASK)) { + SDE_HDMI_DEBUG("removing following mode from list\n"); + drm_mode_debug_printmodeline(mode); + return MODE_BAD; + } + return MODE_OK; } diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c index d8e5c7ed9a68..742e8590c37e 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -124,6 +124,55 @@ static void sde_hdmi_clear_hdr_info(struct drm_bridge *bridge) connector->hdr_supported = false; } +static void sde_hdmi_clear_vsdb_info(struct drm_bridge *bridge) +{ + struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + struct drm_connector *connector = hdmi->connector; + + connector->max_tmds_clock = 0; + connector->latency_present[0] = false; + connector->latency_present[1] = false; + connector->video_latency[0] = false; + connector->video_latency[1] = false; + connector->audio_latency[0] = false; + connector->audio_latency[1] = false; +} + +static void sde_hdmi_clear_hf_vsdb_info(struct drm_bridge *bridge) +{ + struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + struct drm_connector *connector = hdmi->connector; + + connector->max_tmds_char = 0; + connector->scdc_present = false; + connector->rr_capable = false; + connector->supports_scramble = false; + connector->flags_3d = 0; +} + +static void sde_hdmi_clear_vcdb_info(struct drm_bridge *bridge) +{ + struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); + struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + struct drm_connector *connector = hdmi->connector; + + connector->pt_scan_info = 0; + connector->it_scan_info = 0; + connector->ce_scan_info = 0; + connector->rgb_qs = false; + connector->yuv_qs = false; +} + +static void sde_hdmi_clear_vsdbs(struct drm_bridge *bridge) +{ + /* Clear fields of HDMI VSDB */ + sde_hdmi_clear_vsdb_info(bridge); + /* Clear fields of HDMI forum VSDB */ + sde_hdmi_clear_hf_vsdb_info(bridge); +} + static void _sde_hdmi_bridge_power_on(struct drm_bridge *bridge) { struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); @@ -574,13 +623,25 @@ static void _sde_hdmi_bridge_disable(struct drm_bridge *bridge) mutex_lock(&display->display_lock); + if (!bridge) { + SDE_ERROR("Invalid params\n"); + mutex_unlock(&display->display_lock); + return; + } + display->pll_update_enable = false; display->sink_hdcp_ver = SDE_HDMI_HDCP_NONE; + display->sink_hdcp22_support = false; if (sde_hdmi_tx_is_hdcp_enabled(display)) sde_hdmi_hdcp_off(display); sde_hdmi_clear_hdr_info(bridge); + /* Clear HDMI VSDB blocks info */ + sde_hdmi_clear_vsdbs(bridge); + /* Clear HDMI VCDB block info */ + sde_hdmi_clear_vcdb_info(bridge); + mutex_unlock(&display->display_lock); } @@ -906,6 +967,9 @@ static bool _sde_hdmi_bridge_mode_fixup(struct drm_bridge *bridge, struct sde_hdmi_bridge *sde_hdmi_bridge = to_hdmi_bridge(bridge); struct hdmi *hdmi = sde_hdmi_bridge->hdmi; + /* Clear the private flags before assigning new one */ + adjusted_mode->private_flags = 0; + adjusted_mode->private_flags |= _sde_hdmi_choose_best_format(hdmi, adjusted_mode); SDE_DEBUG("Adjusted mode private flags: 0x%x\n", diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.h b/drivers/gpu/drm/msm/hdmi/hdmi.h index 8ca7b36ee0c8..9a0733bf81ff 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.h +++ b/drivers/gpu/drm/msm/hdmi/hdmi.h @@ -50,6 +50,9 @@ struct hdmi { const struct hdmi_platform_config *config; + /* hpd state: */ + bool hpd_off; + /* audio state: */ struct hdmi_audio audio; diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index 0c119ec5d97c..5ebf50575dd6 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2014 Red Hat * Author: Rob Clark * @@ -562,6 +562,11 @@ int msm_atomic_commit(struct drm_device *dev, struct msm_commit *commit; int i, ret; + if (!priv || priv->shutdown_in_progress) { + DRM_ERROR("priv is null or shutdown is in-progress\n"); + return -EINVAL; + } + SDE_ATRACE_BEGIN("atomic_commit"); ret = drm_atomic_helper_prepare_planes(dev, state); if (ret) { diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index c8b11425a817..7b30abfe87b4 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -145,7 +145,8 @@ void __iomem *msm_ioremap(struct platform_device *pdev, const char *name, } if (reglog) - printk(KERN_DEBUG "IO:region %s %p %08lx\n", dbgname, ptr, size); + dev_dbg(&pdev->dev, "IO:region %s %pK %08lx\n", + dbgname, ptr, size); return ptr; } @@ -158,7 +159,7 @@ void msm_iounmap(struct platform_device *pdev, void __iomem *addr) void msm_writel(u32 data, void __iomem *addr) { if (reglog) - printk(KERN_DEBUG "IO:W %p %08x\n", addr, data); + pr_debug("IO:W %pK %08x\n", addr, data); writel(data, addr); } @@ -166,7 +167,7 @@ u32 msm_readl(const void __iomem *addr) { u32 val = readl(addr); if (reglog) - printk(KERN_ERR "IO:R %p %08x\n", addr, val); + pr_err("IO:R %pK %08x\n", addr, val); return val; } @@ -293,7 +294,7 @@ static int msm_unload(struct drm_device *dev) priv->vram.paddr, &attrs); } - sde_evtlog_destroy(); + sde_dbg_destroy(); sde_power_client_destroy(&priv->phandle, priv->pclient); sde_power_resource_deinit(pdev, &priv->phandle); @@ -423,12 +424,19 @@ static int msm_component_bind_all(struct device *dev, } #endif +static int msm_power_enable_wrapper(void *handle, void *client, bool enable) +{ + return sde_power_resource_enable(handle, client, enable); +} + static int msm_load(struct drm_device *dev, unsigned long flags) { struct platform_device *pdev = dev->platformdev; struct msm_drm_private *priv; struct msm_kms *kms; + struct sde_dbg_power_ctrl dbg_power_ctrl = { NULL }; int ret, i; + struct sched_param param; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) { @@ -477,9 +485,13 @@ static int msm_load(struct drm_device *dev, unsigned long flags) if (ret) goto fail; - ret = sde_evtlog_init(dev->primary->debugfs_root); + dbg_power_ctrl.handle = &priv->phandle; + dbg_power_ctrl.client = priv->pclient; + dbg_power_ctrl.enable_fn = msm_power_enable_wrapper; + ret = sde_dbg_init(dev->primary->debugfs_root, &pdev->dev, + &dbg_power_ctrl); if (ret) { - dev_err(dev->dev, "failed to init evtlog: %d\n", ret); + dev_err(dev->dev, "failed to init sde dbg: %d\n", ret); goto fail; } @@ -518,7 +530,12 @@ static int msm_load(struct drm_device *dev, unsigned long flags) goto fail; } } - + /** + * this priority was found during empiric testing to have appropriate + * realtime scheduling to process display updates and interact with + * other real time and normal priority task + */ + param.sched_priority = 16; /* initialize commit thread structure */ for (i = 0; i < priv->num_crtcs; i++) { priv->disp_thread[i].crtc_id = priv->crtcs[i]->base.id; @@ -529,6 +546,11 @@ static int msm_load(struct drm_device *dev, unsigned long flags) &priv->disp_thread[i].worker, "crtc_commit:%d", priv->disp_thread[i].crtc_id); + ret = sched_setscheduler(priv->disp_thread[i].thread, + SCHED_FIFO, ¶m); + if (ret) + pr_warn("display thread priority update failed: %d\n", + ret); if (IS_ERR(priv->disp_thread[i].thread)) { dev_err(dev->dev, "failed to create kthread\n"); @@ -870,7 +892,7 @@ static int msm_enable_vblank(struct drm_device *dev, unsigned int pipe) struct msm_kms *kms = priv->kms; if (!kms) return -ENXIO; - DBG("dev=%p, crtc=%u", dev, pipe); + DBG("dev=%pK, crtc=%u", dev, pipe); return vblank_ctrl_queue_work(priv, pipe, true); } @@ -880,7 +902,7 @@ static void msm_disable_vblank(struct drm_device *dev, unsigned int pipe) struct msm_kms *kms = priv->kms; if (!kms) return; - DBG("dev=%p, crtc=%u", dev, pipe); + DBG("dev=%pK, crtc=%u", dev, pipe); vblank_ctrl_queue_work(priv, pipe, false); } @@ -2181,6 +2203,28 @@ static const struct platform_device_id msm_id[] = { { } }; +static void msm_pdev_shutdown(struct platform_device *pdev) +{ + struct drm_device *ddev = platform_get_drvdata(pdev); + struct msm_drm_private *priv = NULL; + + if (!ddev) { + DRM_ERROR("invalid drm device node\n"); + return; + } + + priv = ddev->dev_private; + if (!priv) { + DRM_ERROR("invalid msm drm private node\n"); + return; + } + + msm_lastclose(ddev); + + /* set this after lastclose to allow kickoff from lastclose */ + priv->shutdown_in_progress = true; +} + static const struct of_device_id dt_match[] = { { .compatible = "qcom,mdp" }, /* mdp4 */ { .compatible = "qcom,sde-kms" }, /* sde */ @@ -2191,6 +2235,7 @@ MODULE_DEVICE_TABLE(of, dt_match); static struct platform_driver msm_platform_driver = { .probe = msm_pdev_probe, .remove = msm_pdev_remove, + .shutdown = msm_pdev_shutdown, .driver = { .name = "msm_drm", .of_match_table = dt_match, diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 25dc5f9ef561..e0ac0582e791 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.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. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -164,6 +164,7 @@ enum msm_mdp_conn_property { CONNECTOR_PROP_TOPOLOGY_NAME, CONNECTOR_PROP_TOPOLOGY_CONTROL, CONNECTOR_PROP_LP, + CONNECTOR_PROP_HPD_OFF, /* total # of properties */ CONNECTOR_PROP_COUNT @@ -374,6 +375,9 @@ struct msm_drm_private { /* list of clients waiting for events */ struct list_head client_event_list; + + /* update the flag when msm driver receives shutdown notification */ + bool shutdown_in_progress; }; struct msm_format { diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c index a3f0392c2f88..8d6d83bf6540 100644 --- a/drivers/gpu/drm/msm/msm_fb.c +++ b/drivers/gpu/drm/msm/msm_fb.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -33,17 +34,33 @@ static int msm_framebuffer_create_handle(struct drm_framebuffer *fb, struct drm_file *file_priv, unsigned int *handle) { - struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); + struct msm_framebuffer *msm_fb; + + if (!fb) { + DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0)); + return -EINVAL; + } + + msm_fb = to_msm_framebuffer(fb); + return drm_gem_handle_create(file_priv, msm_fb->planes[0], handle); } static void msm_framebuffer_destroy(struct drm_framebuffer *fb) { - struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); - int i, n = drm_format_num_planes(fb->pixel_format); + struct msm_framebuffer *msm_fb; + int i, n; + + if (!fb) { + DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0)); + return; + } + + msm_fb = to_msm_framebuffer(fb); + n = drm_format_num_planes(fb->pixel_format); - DBG("destroy: FB ID: %d (%p)", fb->base.id, fb); + DBG("destroy: FB ID: %d (%pK)", fb->base.id, fb); drm_framebuffer_cleanup(fb); @@ -72,9 +89,16 @@ static const struct drm_framebuffer_funcs msm_framebuffer_funcs = { #ifdef CONFIG_DEBUG_FS void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m) { - struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); - int i, n = drm_format_num_planes(fb->pixel_format); + struct msm_framebuffer *msm_fb; + int i, n; + if (!fb) { + DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0)); + return; + } + + msm_fb = to_msm_framebuffer(fb); + n = drm_format_num_planes(fb->pixel_format); seq_printf(m, "fb: %dx%d@%4.4s (%2d, ID:%d)\n", fb->width, fb->height, (char *)&fb->pixel_format, fb->refcount.refcount.counter, fb->base.id); @@ -95,10 +119,17 @@ void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m) int msm_framebuffer_prepare(struct drm_framebuffer *fb, struct msm_gem_address_space *aspace) { - struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); - int ret, i, n = drm_format_num_planes(fb->pixel_format); + struct msm_framebuffer *msm_fb; + int ret, i, n; uint64_t iova; + if (!fb) { + DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0)); + return -EINVAL; + } + + msm_fb = to_msm_framebuffer(fb); + n = drm_format_num_planes(fb->pixel_format); for (i = 0; i < n; i++) { ret = msm_gem_get_iova(msm_fb->planes[i], aspace, &iova); DBG("FB[%u]: iova[%d]: %08llx (%d)", fb->base.id, i, iova, ret); @@ -112,8 +143,16 @@ int msm_framebuffer_prepare(struct drm_framebuffer *fb, void msm_framebuffer_cleanup(struct drm_framebuffer *fb, struct msm_gem_address_space *aspace) { - struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); - int i, n = drm_format_num_planes(fb->pixel_format); + struct msm_framebuffer *msm_fb; + int i, n; + + if (fb == NULL) { + DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0)); + return; + } + + msm_fb = to_msm_framebuffer(fb); + n = drm_format_num_planes(fb->pixel_format); for (i = 0; i < n; i++) msm_gem_put_iova(msm_fb->planes[i], aspace); @@ -123,9 +162,15 @@ void msm_framebuffer_cleanup(struct drm_framebuffer *fb, uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb, struct msm_gem_address_space *aspace, int plane) { - struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); + struct msm_framebuffer *msm_fb; uint64_t iova; + if (!fb) { + DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0)); + return -EINVAL; + } + + msm_fb = to_msm_framebuffer(fb); if (!msm_fb->planes[plane]) return 0; @@ -137,7 +182,14 @@ uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb, struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane) { - struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb); + struct msm_framebuffer *msm_fb; + + if (!fb) { + DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0)); + return ERR_PTR(-EINVAL); + } + + msm_fb = to_msm_framebuffer(fb); return msm_fb->planes[plane]; } @@ -188,7 +240,7 @@ struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, unsigned int hsub, vsub; bool is_modified = false; - DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)", + DBG("create framebuffer: dev=%pK, mode_cmd=%pK (%dx%d@%4.4s)", dev, mode_cmd, mode_cmd->width, mode_cmd->height, (char *)&mode_cmd->pixel_format); @@ -271,7 +323,7 @@ struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, goto fail; } - DBG("create: FB ID: %d (%p)", fb->base.id, fb); + DBG("create: FB ID: %d (%pK)", fb->base.id, fb); return fb; diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index 28b98cc1433c..c71e662d0da1 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -144,7 +144,7 @@ static int msm_fbdev_create(struct drm_fb_helper *helper, goto fail_unlock; } - DBG("fbi=%p, dev=%p", fbi, dev); + DBG("fbi=%pK, dev=%pK", fbi, dev); fbdev->fb = fb; helper->fb = fb; @@ -166,7 +166,7 @@ static int msm_fbdev_create(struct drm_fb_helper *helper, fbi->fix.smem_start = lower_32_bits(paddr); fbi->fix.smem_len = fbdev->bo->size; - DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres); + DBG("par=%pK, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres); DBG("allocated %dx%d fb", fbdev->fb->width, fbdev->fb->height); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index d66071672c62..ce5adef21a00 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -410,7 +410,7 @@ int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) pfn = page_to_pfn(pages[pgoff]); - VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address, + VERB("Inserting %pK pfn %lx, pa %lx", vmf->virtual_address, pfn, pfn << PAGE_SHIFT); ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn); @@ -585,7 +585,7 @@ int msm_gem_get_iova(struct drm_gem_object *obj, obj_remove_domain(domain); mutex_unlock(&msm_obj->lock); - return 0; + return ret; } /* get iova without taking a reference, used in places where you have @@ -770,7 +770,7 @@ void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m) uint64_t off = drm_vma_node_start(&obj->vma_node); WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - seq_printf(m, "%08x: %c(r=%u,w=%u) %2d (%2d) %08llx %p\t", + seq_printf(m, "%08x: %c(r=%u,w=%u) %2d (%2d) %08llx %pK\t", msm_obj->flags, is_active(msm_obj) ? 'A' : 'I', msm_obj->read_fence, msm_obj->write_fence, obj->name, obj->refcount.refcount.counter, diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index f399d24019e4..a5a768a63858 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -60,7 +60,7 @@ msm_gem_address_space_new(struct msm_mmu *mmu, const char *name, if (aspace->va_len) drm_mm_init(&aspace->mm, (start >> PAGE_SHIFT), - (end >> PAGE_SHIFT) - 1); + (aspace->va_len >> PAGE_SHIFT)); kref_init(&aspace->kref); diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 7c109fdab545..03c2fc284221 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -2,6 +2,8 @@ * Copyright (C) 2013 Red Hat * Author: Rob Clark * + * 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 as published by * the Free Software Foundation. @@ -15,6 +17,8 @@ * this program. If not, see . */ +#include +#include #include "msm_gpu.h" #include "msm_gem.h" #include "msm_mmu.h" @@ -24,6 +28,8 @@ * Power Management: */ +#define ACTIVE_POWER_LEVEL(gpu) min_t(unsigned int, 2, gpu->nr_pwrlevels - 3) + #ifdef DOWNSTREAM_CONFIG_MSM_BUS_SCALING #include static void bs_init(struct msm_gpu *gpu) @@ -55,6 +61,138 @@ static void bs_fini(struct msm_gpu *gpu) {} static void bs_set(struct msm_gpu *gpu, int idx) {} #endif +static int msm_devfreq_target(struct device *dev, unsigned long *freq, + u32 flags) +{ + struct msm_gpu *gpu = platform_get_drvdata(to_platform_device(dev)); + struct dev_pm_opp *opp; + + opp = devfreq_recommended_opp(dev, freq, flags); + + if (IS_ERR(opp)) + return PTR_ERR(opp); + + clk_set_rate(gpu->core_clk, *freq); + + return 0; +} + +static int msm_devfreq_get_dev_status(struct device *dev, + struct devfreq_dev_status *status) +{ + struct msm_gpu *gpu = platform_get_drvdata(to_platform_device(dev)); + uint64_t cycles; + ktime_t time; + u32 freq; + + status->current_frequency = (unsigned long) clk_get_rate(gpu->core_clk); + + cycles = gpu->funcs->gpu_busy(gpu); + freq = ((u32) status->current_frequency) / 1000000; + status->busy_time = ((u32) (cycles - gpu->devfreq.busy_cycles)) / freq; + gpu->devfreq.busy_cycles = cycles; + + time = ktime_get(); + status->total_time = ktime_us_delta(time, gpu->devfreq.time); + gpu->devfreq.time = time; + + return 0; +} + +static int msm_devfreq_get_cur_freq(struct device *dev, unsigned long *freq) +{ + struct msm_gpu *gpu = platform_get_drvdata(to_platform_device(dev)); + + *freq = clk_get_rate(gpu->core_clk); + return 0; +} + +static void msm_devfreq_manage_opp_notifier(struct device *dev, + struct notifier_block *nb, bool subscribe) +{ + struct srcu_notifier_head *nh; + + rcu_read_lock(); + nh = dev_pm_opp_get_notifier(dev); + if (IS_ERR(nh)) { + rcu_read_unlock(); + return; + } + rcu_read_unlock(); + + if (subscribe) + srcu_notifier_chain_register(nh, nb); + else + srcu_notifier_chain_unregister(nh, nb); +} + +static int msm_opp_notify(struct notifier_block *nb, unsigned long type, + void *in_opp) +{ + struct msm_gpu *gpu = container_of(nb, struct msm_gpu, nb); + + if (type != OPP_EVENT_ENABLE && type != OPP_EVENT_DISABLE) + return -EINVAL; + + /* + * The opp table for the GPU device changed, call update_devfreq() + * to adjust the GPU frequency if needed + */ + mutex_lock(&gpu->devfreq.devfreq->lock); + update_devfreq(gpu->devfreq.devfreq); + mutex_unlock(&gpu->devfreq.devfreq->lock); + + return 0; +} + +static struct devfreq_dev_profile msm_devfreq_profile = { + .polling_ms = 10, + .target = msm_devfreq_target, + .get_dev_status = msm_devfreq_get_dev_status, + .get_cur_freq = msm_devfreq_get_cur_freq, +}; + +static void msm_devfreq_init(struct msm_gpu *gpu) +{ + struct msm_drm_private *priv = gpu->dev->dev_private; + struct device *dev = &priv->gpu_pdev->dev; + unsigned int level = min_t(unsigned int, 2, gpu->nr_pwrlevels - 3); + + /* Don't do devfreq if the GPU doesn't implement statistics gathering */ + if (!gpu->funcs->gpu_busy) + return; + + msm_devfreq_profile.initial_freq = gpu->gpufreq[level]; + msm_devfreq_profile.freq_table = gpu->gpufreq; + msm_devfreq_profile.max_state = gpu->nr_pwrlevels - 1; + + gpu->devfreq.devfreq = devm_devfreq_add_device(dev, + &msm_devfreq_profile, "simple_ondemand", NULL); + + if (IS_ERR(gpu->devfreq.devfreq)) { + dev_err(dev, "Couldn't initialize GPU devfreq\n"); + gpu->devfreq.devfreq = NULL; + return; + } + + gpu->devfreq.cooling_dev = of_devfreq_cooling_register(dev->of_node, + gpu->devfreq.devfreq); + + if (IS_ERR(gpu->devfreq.cooling_dev)) { + dev_err(dev, "Couldn't register GPU devfreq cooling device\n"); + gpu->devfreq.cooling_dev = NULL; + return; + } + + gpu->nb.notifier_call = msm_opp_notify; + + /* + * register for OPP notifcations so we can adjust the + * GPU device power levels appropriately + */ + msm_devfreq_manage_opp_notifier(dev, &gpu->nb, true); +} + static int enable_pwrrail(struct msm_gpu *gpu) { struct drm_device *dev = gpu->dev; @@ -90,7 +228,8 @@ static int disable_pwrrail(struct msm_gpu *gpu) static int enable_clk(struct msm_gpu *gpu) { - uint32_t rate = gpu->gpufreq[gpu->active_level]; + unsigned int level = ACTIVE_POWER_LEVEL(gpu); + uint32_t rate = gpu->gpufreq[level]; int i; if (gpu->core_clk) @@ -134,11 +273,12 @@ static int disable_clk(struct msm_gpu *gpu) static int enable_axi(struct msm_gpu *gpu) { + unsigned int level = ACTIVE_POWER_LEVEL(gpu); if (gpu->ebi1_clk) clk_prepare_enable(gpu->ebi1_clk); - if (gpu->busfreq[gpu->active_level]) - bs_set(gpu, gpu->busfreq[gpu->active_level]); + if (gpu->busfreq[level]) + bs_set(gpu, gpu->busfreq[level]); return 0; } @@ -147,11 +287,21 @@ static int disable_axi(struct msm_gpu *gpu) if (gpu->ebi1_clk) clk_disable_unprepare(gpu->ebi1_clk); - if (gpu->busfreq[gpu->active_level]) + if (gpu->busfreq[gpu->nr_pwrlevels - 1]) bs_set(gpu, 0); return 0; } +static void msm_devfreq_resume(struct msm_gpu *gpu) +{ + if (gpu->devfreq.devfreq) { + gpu->devfreq.busy_cycles = 0; + gpu->devfreq.time = ktime_get(); + + devfreq_resume_device(gpu->devfreq.devfreq); + } +} + int msm_gpu_pm_resume(struct msm_gpu *gpu) { struct drm_device *dev = gpu->dev; @@ -186,6 +336,8 @@ int msm_gpu_pm_resume(struct msm_gpu *gpu) if (gpu->aspace && gpu->aspace->mmu) msm_mmu_enable(gpu->aspace->mmu); + msm_devfreq_resume(gpu); + return 0; } @@ -206,6 +358,9 @@ int msm_gpu_pm_suspend(struct msm_gpu *gpu) if (WARN_ON(gpu->active_cnt < 0)) return -EINVAL; + if (gpu->devfreq.devfreq) + devfreq_suspend_device(gpu->devfreq.devfreq); + if (gpu->aspace && gpu->aspace->mmu) msm_mmu_disable(gpu->aspace->mmu); @@ -931,6 +1086,10 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, if (IS_ERR(gpu->gpu_cx)) gpu->gpu_cx = NULL; + platform_set_drvdata(pdev, gpu); + + msm_devfreq_init(gpu); + gpu->aspace = msm_gpu_create_address_space(gpu, &pdev->dev, MSM_IOMMU_DOMAIN_USER, config->va_start, config->va_end, "gpu"); @@ -1015,6 +1174,12 @@ void msm_gpu_cleanup(struct msm_gpu *gpu) WARN_ON(!list_empty(&gpu->active_list)); + if (gpu->devfreq.devfreq) { + msm_devfreq_manage_opp_notifier(&pdev->dev, &gpu->nb, false); + devfreq_cooling_unregister(gpu->devfreq.cooling_dev); + devfreq_remove_device(gpu->devfreq.devfreq); + } + if (gpu->irq >= 0) { disable_irq(gpu->irq); devm_free_irq(&pdev->dev, gpu->irq, gpu); diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index eeebfb746f7f..64bf734ecd19 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -2,6 +2,8 @@ * Copyright (C) 2013 Red Hat * Author: Rob Clark * + * 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 as published by * the Free Software Foundation. @@ -21,6 +23,7 @@ #include #include #include +#include #include "msm_drv.h" #include "msm_ringbuffer.h" @@ -78,6 +81,7 @@ struct msm_gpu_funcs { u32 *lo, u32 *hi); void (*put_counter)(struct msm_gpu *gpu, u32 groupid, int counterid); u64 (*read_counter)(struct msm_gpu *gpu, u32 groupid, int counterid); + u64 (*gpu_busy)(struct msm_gpu *gpu); }; struct msm_gpu { @@ -125,7 +129,6 @@ struct msm_gpu { uint32_t gpufreq[10]; uint32_t busfreq[10]; uint32_t nr_pwrlevels; - uint32_t active_level; struct pm_qos_request pm_qos_req_dma; @@ -147,6 +150,15 @@ struct msm_gpu { struct timer_list hangcheck_timer; struct work_struct recover_work; struct msm_snapshot *snapshot; + + struct { + struct devfreq *devfreq; + u64 busy_cycles; + ktime_t time; + struct thermal_cooling_device *cooling_dev; + } devfreq; + + struct notifier_block nb; }; struct msm_gpu_submitqueue { diff --git a/drivers/gpu/drm/msm/msm_prop.c b/drivers/gpu/drm/msm/msm_prop.c index 10f89de25831..02ed2b7a062f 100644 --- a/drivers/gpu/drm/msm/msm_prop.c +++ b/drivers/gpu/drm/msm/msm_prop.c @@ -340,9 +340,16 @@ void msm_property_install_enum(struct msm_property_info *info, info->property_data[property_idx].default_value = default_value; info->property_data[property_idx].force_dirty = false; + /* select first defined value for enums */ + if (!is_bitmask) + info->property_data[property_idx].default_value = + values->type; + /* always attach property, if created */ if (*prop) { - drm_object_attach_property(info->base, *prop, 0); + drm_object_attach_property(info->base, *prop, + info->property_data + [property_idx].default_value); ++info->install_count; } } diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c index 8d15fd2f9cf7..84b0108438fa 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 @@ -45,6 +45,11 @@ static const struct drm_prop_enum_list e_power_mode[] = { {SDE_MODE_DPMS_OFF, "OFF"}, }; +static const struct drm_prop_enum_list hpd_clock_state[] = { + {SDE_MODE_HPD_ON, "ON"}, + {SDE_MODE_HPD_OFF, "OFF"}, +}; + int sde_connector_get_info(struct drm_connector *connector, struct msm_display_info *info) { @@ -474,6 +479,9 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector, _sde_connector_update_power_locked(c_conn); mutex_unlock(&c_conn->lock); break; + case CONNECTOR_PROP_HPD_OFF: + c_conn->hpd_mode = val; + break; default: break; } @@ -802,6 +810,7 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, c_conn->display = display; c_conn->dpms_mode = DRM_MODE_DPMS_ON; + c_conn->hpd_mode = SDE_MODE_HPD_ON; c_conn->lp_mode = 0; c_conn->last_panel_power_mode = SDE_MODE_DPMS_ON; @@ -929,6 +938,11 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, ARRAY_SIZE(e_power_mode), CONNECTOR_PROP_LP, 0); + msm_property_install_enum(&c_conn->property_info, "HPD_OFF", + DRM_MODE_PROP_ATOMIC, 0, hpd_clock_state, + ARRAY_SIZE(hpd_clock_state), + CONNECTOR_PROP_HPD_OFF, 0); + rc = msm_property_install_get_status(&c_conn->property_info); if (rc) { SDE_ERROR("failed to create one or more properties\n"); diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h index f9b8c3966d74..a0ddcf444540 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 @@ -22,6 +22,9 @@ #include "sde_kms.h" #include "sde_fence.h" +#define SDE_MODE_HPD_ON 0 +#define SDE_MODE_HPD_OFF 1 + #define SDE_CONNECTOR_NAME_SIZE 16 struct sde_connector; @@ -207,6 +210,7 @@ struct sde_connector { struct sde_fence retire_fence; struct sde_connector_ops ops; int dpms_mode; + u64 hpd_mode; int lp_mode; int last_panel_power_mode; diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.c b/drivers/gpu/drm/msm/sde/sde_core_irq.c index dbfc2dd11a17..4f7e688650de 100644 --- a/drivers/gpu/drm/msm/sde/sde_core_irq.c +++ b/drivers/gpu/drm/msm/sde/sde_core_irq.c @@ -31,23 +31,35 @@ static void sde_core_irq_callback_handler(void *arg, int irq_idx) struct sde_irq *irq_obj = &sde_kms->irq_obj; struct sde_irq_callback *cb; unsigned long irq_flags; + bool cb_tbl_error = false; + int enable_counts = 0; - SDE_DEBUG("irq_idx=%d\n", irq_idx); + pr_debug("irq_idx=%d\n", irq_idx); - if (list_empty(&irq_obj->irq_cb_tbl[irq_idx])) - SDE_ERROR("irq_idx=%d has no registered callback\n", irq_idx); + spin_lock_irqsave(&sde_kms->irq_obj.cb_lock, irq_flags); + if (list_empty(&irq_obj->irq_cb_tbl[irq_idx])) { + /* print error outside lock */ + cb_tbl_error = true; + enable_counts = atomic_read( + &sde_kms->irq_obj.enable_counts[irq_idx]); + } atomic_inc(&irq_obj->irq_counts[irq_idx]); /* * Perform registered function callback */ - spin_lock_irqsave(&sde_kms->irq_obj.cb_lock, irq_flags); list_for_each_entry(cb, &irq_obj->irq_cb_tbl[irq_idx], list) if (cb->func) cb->func(cb->arg, irq_idx); spin_unlock_irqrestore(&sde_kms->irq_obj.cb_lock, irq_flags); + if (cb_tbl_error) { + SDE_ERROR("irq has no registered callback, idx %d enables %d\n", + irq_idx, enable_counts); + SDE_EVT32_IRQ(irq_idx, enable_counts, SDE_EVTLOG_ERROR); + } + /* * Clear pending interrupt status in HW. * NOTE: sde_core_irq_callback_handler is protected by top-level diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index 66b03522903f..7c18ab52c88b 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.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 * @@ -1341,8 +1341,7 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc, struct drm_display_mode *mode; int cnt = 0, rc = 0, mixer_width, i, z_pos; - int left_crtc_zpos_cnt[SDE_STAGE_MAX] = {0}; - int right_crtc_zpos_cnt[SDE_STAGE_MAX] = {0}; + int left_zpos_cnt = 0, right_zpos_cnt = 0; if (!crtc) { SDE_ERROR("invalid crtc\n"); @@ -1396,11 +1395,12 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc, } } + /* assign mixer stages based on sorted zpos property */ + sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL); + if (!sde_is_custom_client()) { int stage_old = pstates[0].stage; - /* assign mixer stages based on sorted zpos property */ - sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL); z_pos = 0; for (i = 0; i < cnt; i++) { if (stage_old != pstates[i].stage) @@ -1410,8 +1410,14 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc, } } + z_pos = -1; for (i = 0; i < cnt; i++) { - z_pos = pstates[i].stage; + /* reset counts at every new blend stage */ + if (pstates[i].stage != z_pos) { + left_zpos_cnt = 0; + right_zpos_cnt = 0; + z_pos = pstates[i].stage; + } /* verify z_pos setting before using it */ if (z_pos >= SDE_STAGE_MAX - SDE_STAGE_0) { @@ -1420,22 +1426,24 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc, rc = -EINVAL; goto end; } else if (pstates[i].drm_pstate->crtc_x < mixer_width) { - if (left_crtc_zpos_cnt[z_pos] == 2) { - SDE_ERROR("> 2 plane @ stage%d on left\n", + if (left_zpos_cnt == 2) { + SDE_ERROR("> 2 planes @ stage %d on left\n", z_pos); rc = -EINVAL; goto end; } - left_crtc_zpos_cnt[z_pos]++; + left_zpos_cnt++; + } else { - if (right_crtc_zpos_cnt[z_pos] == 2) { - SDE_ERROR("> 2 plane @ stage%d on right\n", + if (right_zpos_cnt == 2) { + SDE_ERROR("> 2 planes @ stage %d on right\n", z_pos); rc = -EINVAL; goto end; } - right_crtc_zpos_cnt[z_pos]++; + right_zpos_cnt++; } + pstates[i].sde_pstate->stage = z_pos + SDE_STAGE_0; SDE_DEBUG("%s: zpos %d", sde_crtc->name, z_pos); } @@ -1447,6 +1455,73 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc, goto end; } + /* validate source split: + * use pstates sorted by stage to check planes on same stage + * we assume that all pipes are in source split so its valid to compare + * without taking into account left/right mixer placement + */ + for (i = 1; i < cnt; i++) { + struct plane_state *prv_pstate, *cur_pstate; + struct sde_rect left_rect, right_rect; + int32_t left_pid, right_pid; + int32_t stage; + + prv_pstate = &pstates[i - 1]; + cur_pstate = &pstates[i]; + if (prv_pstate->stage != cur_pstate->stage) + continue; + + stage = cur_pstate->stage; + + left_pid = prv_pstate->sde_pstate->base.plane->base.id; + POPULATE_RECT(&left_rect, prv_pstate->drm_pstate->crtc_x, + prv_pstate->drm_pstate->crtc_y, + prv_pstate->drm_pstate->crtc_w, + prv_pstate->drm_pstate->crtc_h, false); + + right_pid = cur_pstate->sde_pstate->base.plane->base.id; + POPULATE_RECT(&right_rect, cur_pstate->drm_pstate->crtc_x, + cur_pstate->drm_pstate->crtc_y, + cur_pstate->drm_pstate->crtc_w, + cur_pstate->drm_pstate->crtc_h, false); + + if (right_rect.x < left_rect.x) { + swap(left_pid, right_pid); + swap(left_rect, right_rect); + } + + /** + * - planes are enumerated in pipe-priority order such that + * planes with lower drm_id must be left-most in a shared + * blend-stage when using source split. + * - planes in source split must be contiguous in width + * - planes in source split must have same dest yoff and height + */ + if (right_pid < left_pid) { + SDE_ERROR( + "invalid src split cfg. priority mismatch. stage: %d left: %d right: %d\n", + stage, left_pid, right_pid); + rc = -EINVAL; + goto end; + } else if (right_rect.x != (left_rect.x + left_rect.w)) { + SDE_ERROR( + "non-contiguous coordinates for src split. stage: %d left: %d - %d right: %d - %d\n", + stage, left_rect.x, left_rect.w, + right_rect.x, right_rect.w); + rc = -EINVAL; + goto end; + } else if ((left_rect.y != right_rect.y) || + (left_rect.h != right_rect.h)) { + SDE_ERROR( + "source split at stage: %d. invalid yoff/height: l_y: %d r_y: %d l_h: %d r_h: %d\n", + stage, left_rect.y, right_rect.y, + left_rect.h, right_rect.h); + rc = -EINVAL; + goto end; + } + } + + end: return rc; } @@ -1483,7 +1558,7 @@ void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc, { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); - SDE_DEBUG("%s: cancel: %p\n", sde_crtc->name, file); + SDE_DEBUG("%s: cancel: %pK\n", sde_crtc->name, file); _sde_crtc_complete_flip(crtc, file); } diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c index cb8b349e72c7..fa17768d9939 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 * @@ -43,6 +43,8 @@ /* timeout in frames waiting for frame done */ #define SDE_ENCODER_FRAME_DONE_TIMEOUT 60 +#define MISR_BUFF_SIZE 256 + /* * Two to anticipate panels that can do cmd/vid dynamic switching * plan is to create all possible physical encoder types, and switch between @@ -600,6 +602,12 @@ static void sde_encoder_underrun_callback(struct drm_encoder *drm_enc, SDE_ATRACE_BEGIN("encoder_underrun_callback"); atomic_inc(&phy_enc->underrun_cnt); SDE_EVT32(DRMID(drm_enc), atomic_read(&phy_enc->underrun_cnt)); + + trace_sde_encoder_underrun(DRMID(drm_enc), + atomic_read(&phy_enc->underrun_cnt)); + SDE_DBG_CTRL("stop_ftrace"); + SDE_DBG_CTRL("panic_underrun"); + SDE_ATRACE_END("encoder_underrun_callback"); } @@ -1046,16 +1054,18 @@ static ssize_t _sde_encoder_misr_set(struct file *file, struct sde_encoder_virt *sde_enc; struct drm_encoder *drm_enc; int i = 0; - char buf[10]; + char buf[MISR_BUFF_SIZE + 1]; + size_t buff_copy; u32 enable, frame_count; drm_enc = file->private_data; sde_enc = to_sde_encoder_virt(drm_enc); - if (copy_from_user(buf, user_buf, count)) - return -EFAULT; + buff_copy = min_t(size_t, MISR_BUFF_SIZE, count); + if (copy_from_user(buf, user_buf, buff_copy)) + return -EINVAL; - buf[count] = 0; /* end of string */ + buf[buff_copy] = 0; /* end of string */ if (sscanf(buf, "%u %u", &enable, &frame_count) != 2) return -EFAULT; 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 a22287421360..2f89c571fcfc 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c @@ -281,23 +281,40 @@ static void sde_encoder_phys_vid_vblank_irq(void *arg, int irq_idx) { struct sde_encoder_phys_vid *vid_enc = arg; struct sde_encoder_phys *phys_enc; + struct sde_hw_ctl *hw_ctl; unsigned long lock_flags; - int new_cnt; + u32 flush_register = 0; + int new_cnt = -1, old_cnt = -1; if (!vid_enc) return; phys_enc = &vid_enc->base; + hw_ctl = phys_enc->hw_ctl; + if (phys_enc->parent_ops.handle_vblank_virt) phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent, phys_enc); + old_cnt = atomic_read(&phys_enc->pending_kickoff_cnt); + + /* + * only decrement the pending flush count if we've actually flushed + * hardware. due to sw irq latency, vblank may have already happened + * so we need to double-check with hw that it accepted the flush bits + */ spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags); - new_cnt = atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0); - SDE_EVT32_IRQ(DRMID(phys_enc->parent), vid_enc->hw_intf->idx - INTF_0, - new_cnt); + if (hw_ctl && hw_ctl->ops.get_flush_register) + flush_register = hw_ctl->ops.get_flush_register(hw_ctl); + + if (flush_register == 0) + new_cnt = atomic_add_unless(&phys_enc->pending_kickoff_cnt, + -1, 0); spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags); + SDE_EVT32_IRQ(DRMID(phys_enc->parent), vid_enc->hw_intf->idx - INTF_0, + old_cnt, new_cnt, flush_register); + /* Signal any waiting atomic commit thread */ wake_up_all(&phys_enc->pending_kickoff_wq); } @@ -316,10 +333,24 @@ static void sde_encoder_phys_vid_underrun_irq(void *arg, int irq_idx) phys_enc); } +static bool _sde_encoder_phys_is_ppsplit(struct sde_encoder_phys *phys_enc) +{ + enum sde_rm_topology_name topology; + + if (!phys_enc) + return false; + + topology = sde_connector_get_topology_name(phys_enc->connector); + if (topology == SDE_RM_TOPOLOGY_PPSPLIT) + return true; + + return false; +} + static bool sde_encoder_phys_vid_needs_single_flush( struct sde_encoder_phys *phys_enc) { - return phys_enc && phys_enc->split_role != ENC_ROLE_SOLO; + return phys_enc && _sde_encoder_phys_is_ppsplit(phys_enc); } static int sde_encoder_phys_vid_register_irq(struct sde_encoder_phys *phys_enc, @@ -657,7 +688,7 @@ static int sde_encoder_phys_vid_wait_for_vblank( KICKOFF_TIMEOUT_MS); if (ret <= 0) { irq_status = sde_core_irq_read(phys_enc->sde_kms, - INTR_IDX_VSYNC, true); + vid_enc->irq_idx[INTR_IDX_VSYNC], true); if (irq_status) { SDE_EVT32(DRMID(phys_enc->parent), vid_enc->hw_intf->idx - INTF_0); diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c index 2187d221a352..340cba536367 100644 --- a/drivers/gpu/drm/msm/sde/sde_formats.c +++ b/drivers/gpu/drm/msm/sde/sde_formats.c @@ -22,6 +22,11 @@ #define SDE_UBWC_META_BLOCK_SIZE 256 #define SDE_UBWC_PLANE_SIZE_ALIGNMENT 4096 +#define SDE_TILE_HEIGHT_DEFAULT 1 +#define SDE_TILE_HEIGHT_TILED 4 +#define SDE_TILE_HEIGHT_UBWC 4 +#define SDE_TILE_HEIGHT_NV12 8 + #define SDE_MAX_IMG_WIDTH 0x3FFF #define SDE_MAX_IMG_HEIGHT 0x3FFF @@ -48,9 +53,30 @@ bp, flg, fm, np) \ .bpp = bp, \ .fetch_mode = fm, \ .flag = {(flg)}, \ - .num_planes = np \ + .num_planes = np, \ + .tile_height = SDE_TILE_HEIGHT_DEFAULT \ } +#define INTERLEAVED_RGB_FMT_TILED(fmt, a, r, g, b, e0, e1, e2, e3, uc, \ +alpha, bp, flg, fm, np, th) \ +{ \ + .base.pixel_format = DRM_FORMAT_ ## fmt, \ + .fetch_planes = SDE_PLANE_INTERLEAVED, \ + .alpha_enable = alpha, \ + .element = { (e0), (e1), (e2), (e3) }, \ + .bits = { g, b, r, a }, \ + .chroma_sample = SDE_CHROMA_RGB, \ + .unpack_align_msb = 0, \ + .unpack_tight = 1, \ + .unpack_count = uc, \ + .bpp = bp, \ + .fetch_mode = fm, \ + .flag = {(flg)}, \ + .num_planes = np, \ + .tile_height = th \ +} + + #define INTERLEAVED_YUV_FMT(fmt, a, r, g, b, e0, e1, e2, e3, \ alpha, chroma, count, bp, flg, fm, np) \ { \ @@ -66,7 +92,8 @@ alpha, chroma, count, bp, flg, fm, np) \ .bpp = bp, \ .fetch_mode = fm, \ .flag = {(flg)}, \ - .num_planes = np \ + .num_planes = np, \ + .tile_height = SDE_TILE_HEIGHT_DEFAULT \ } #define PSEUDO_YUV_FMT(fmt, a, r, g, b, e0, e1, chroma, flg, fm, np) \ @@ -83,7 +110,27 @@ alpha, chroma, count, bp, flg, fm, np) \ .bpp = 2, \ .fetch_mode = fm, \ .flag = {(flg)}, \ - .num_planes = np \ + .num_planes = np, \ + .tile_height = SDE_TILE_HEIGHT_DEFAULT \ +} + +#define PSEUDO_YUV_FMT_TILED(fmt, a, r, g, b, e0, e1, chroma, \ +flg, fm, np, th) \ +{ \ + .base.pixel_format = DRM_FORMAT_ ## fmt, \ + .fetch_planes = SDE_PLANE_PSEUDO_PLANAR, \ + .alpha_enable = false, \ + .element = { (e0), (e1), 0, 0 }, \ + .bits = { g, b, r, a }, \ + .chroma_sample = chroma, \ + .unpack_align_msb = 0, \ + .unpack_tight = 1, \ + .unpack_count = 2, \ + .bpp = 2, \ + .fetch_mode = fm, \ + .flag = {(flg)}, \ + .num_planes = np, \ + .tile_height = th \ } #define PSEUDO_YUV_FMT_LOOSE(fmt, a, r, g, b, e0, e1, chroma, flg, fm, np)\ @@ -100,9 +147,30 @@ alpha, chroma, count, bp, flg, fm, np) \ .bpp = 2, \ .fetch_mode = fm, \ .flag = {(flg)}, \ - .num_planes = np \ + .num_planes = np, \ + .tile_height = SDE_TILE_HEIGHT_DEFAULT \ } +#define PSEUDO_YUV_FMT_LOOSE_TILED(fmt, a, r, g, b, e0, e1, chroma, \ +flg, fm, np, th) \ +{ \ + .base.pixel_format = DRM_FORMAT_ ## fmt, \ + .fetch_planes = SDE_PLANE_PSEUDO_PLANAR, \ + .alpha_enable = false, \ + .element = { (e0), (e1), 0, 0 }, \ + .bits = { g, b, r, a }, \ + .chroma_sample = chroma, \ + .unpack_align_msb = 1, \ + .unpack_tight = 0, \ + .unpack_count = 2, \ + .bpp = 2, \ + .fetch_mode = fm, \ + .flag = {(flg)}, \ + .num_planes = np, \ + .tile_height = th \ +} + + #define PLANAR_YUV_FMT(fmt, a, r, g, b, e0, e1, e2, alpha, chroma, bp, \ flg, fm, np) \ { \ @@ -118,7 +186,8 @@ flg, fm, np) \ .bpp = bp, \ .fetch_mode = fm, \ .flag = {(flg)}, \ - .num_planes = np \ + .num_planes = np, \ + .tile_height = SDE_TILE_HEIGHT_DEFAULT \ } /* @@ -414,75 +483,99 @@ static const struct sde_format sde_format_map[] = { * These tables hold the A5x tile formats supported. */ static const struct sde_format sde_format_map_tile[] = { - INTERLEAVED_RGB_FMT(ARGB8888, + INTERLEAVED_RGB_FMT_TILED(BGR565, + 0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT, + C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3, + false, 2, 0, + SDE_FETCH_UBWC, 1, SDE_TILE_HEIGHT_TILED), + + INTERLEAVED_RGB_FMT_TILED(ARGB8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, true, 4, 0, - SDE_FETCH_UBWC, 1), + SDE_FETCH_UBWC, 1, SDE_TILE_HEIGHT_TILED), - INTERLEAVED_RGB_FMT(ABGR8888, + INTERLEAVED_RGB_FMT_TILED(ABGR8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4, true, 4, 0, - SDE_FETCH_UBWC, 1), + SDE_FETCH_UBWC, 1, SDE_TILE_HEIGHT_TILED), - INTERLEAVED_RGB_FMT(RGBA8888, + INTERLEAVED_RGB_FMT_TILED(XBGR8888, + COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + false, 4, 0, + SDE_FETCH_UBWC, 1, SDE_TILE_HEIGHT_TILED), + + INTERLEAVED_RGB_FMT_TILED(RGBA8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, true, 4, 0, - SDE_FETCH_UBWC, 1), + SDE_FETCH_UBWC, 1, SDE_TILE_HEIGHT_TILED), - INTERLEAVED_RGB_FMT(BGRA8888, + INTERLEAVED_RGB_FMT_TILED(BGRA8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, true, 4, 0, - SDE_FETCH_UBWC, 1), + SDE_FETCH_UBWC, 1, SDE_TILE_HEIGHT_TILED), - INTERLEAVED_RGB_FMT(BGRX8888, + INTERLEAVED_RGB_FMT_TILED(BGRX8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4, false, 4, 0, - SDE_FETCH_UBWC, 1), + SDE_FETCH_UBWC, 1, SDE_TILE_HEIGHT_TILED), - INTERLEAVED_RGB_FMT(XRGB8888, + INTERLEAVED_RGB_FMT_TILED(XRGB8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4, false, 4, 0, - SDE_FETCH_UBWC, 1), + SDE_FETCH_UBWC, 1, SDE_TILE_HEIGHT_TILED), - INTERLEAVED_RGB_FMT(RGBX8888, + INTERLEAVED_RGB_FMT_TILED(RGBX8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, false, 4, 0, - SDE_FETCH_UBWC, 1), + SDE_FETCH_UBWC, 1, SDE_TILE_HEIGHT_TILED), - PSEUDO_YUV_FMT(NV12, + INTERLEAVED_RGB_FMT_TILED(ABGR2101010, + COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + true, 4, SDE_FORMAT_FLAG_DX, + SDE_FETCH_UBWC, 1, SDE_TILE_HEIGHT_TILED), + + INTERLEAVED_RGB_FMT_TILED(XBGR2101010, + COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, + C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, + true, 4, SDE_FORMAT_FLAG_DX, + SDE_FETCH_UBWC, 1, SDE_TILE_HEIGHT_TILED), + + PSEUDO_YUV_FMT_TILED(NV12, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C1_B_Cb, C2_R_Cr, SDE_CHROMA_420, SDE_FORMAT_FLAG_YUV, - SDE_FETCH_UBWC, 2), + SDE_FETCH_UBWC, 2, SDE_TILE_HEIGHT_NV12), - PSEUDO_YUV_FMT(NV21, + PSEUDO_YUV_FMT_TILED(NV21, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C1_B_Cb, SDE_CHROMA_420, SDE_FORMAT_FLAG_YUV, - SDE_FETCH_UBWC, 2), + SDE_FETCH_UBWC, 2, SDE_TILE_HEIGHT_NV12), }; static const struct sde_format sde_format_map_p010_tile[] = { - PSEUDO_YUV_FMT_LOOSE(NV12, + PSEUDO_YUV_FMT_LOOSE_TILED(NV12, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C1_B_Cb, C2_R_Cr, SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX), - SDE_FETCH_UBWC, 2), + SDE_FETCH_UBWC, 2, SDE_TILE_HEIGHT_NV12), }; static const struct sde_format sde_format_map_tp10_tile[] = { - PSEUDO_YUV_FMT(NV12, + PSEUDO_YUV_FMT_TILED(NV12, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C1_B_Cb, C2_R_Cr, SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX), - SDE_FETCH_UBWC, 2), + SDE_FETCH_UBWC, 2, SDE_TILE_HEIGHT_NV12), }; /* @@ -492,42 +585,42 @@ static const struct sde_format sde_format_map_tp10_tile[] = { * the data will be passed by user-space. */ static const struct sde_format sde_format_map_ubwc[] = { - INTERLEAVED_RGB_FMT(BGR565, + INTERLEAVED_RGB_FMT_TILED(BGR565, 0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3, false, 2, SDE_FORMAT_FLAG_COMPRESSED, - SDE_FETCH_UBWC, 2), + SDE_FETCH_UBWC, 2, SDE_TILE_HEIGHT_UBWC), - INTERLEAVED_RGB_FMT(ABGR8888, + INTERLEAVED_RGB_FMT_TILED(ABGR8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, true, 4, SDE_FORMAT_FLAG_COMPRESSED, - SDE_FETCH_UBWC, 2), + SDE_FETCH_UBWC, 2, SDE_TILE_HEIGHT_UBWC), - INTERLEAVED_RGB_FMT(XBGR8888, + INTERLEAVED_RGB_FMT_TILED(XBGR8888, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, false, 4, SDE_FORMAT_FLAG_COMPRESSED, - SDE_FETCH_UBWC, 2), + SDE_FETCH_UBWC, 2, SDE_TILE_HEIGHT_UBWC), - INTERLEAVED_RGB_FMT(ABGR2101010, + INTERLEAVED_RGB_FMT_TILED(ABGR2101010, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, true, 4, SDE_FORMAT_FLAG_DX | SDE_FORMAT_FLAG_COMPRESSED, - SDE_FETCH_UBWC, 2), + SDE_FETCH_UBWC, 2, SDE_TILE_HEIGHT_UBWC), - INTERLEAVED_RGB_FMT(XBGR2101010, + INTERLEAVED_RGB_FMT_TILED(XBGR2101010, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4, true, 4, SDE_FORMAT_FLAG_DX | SDE_FORMAT_FLAG_COMPRESSED, - SDE_FETCH_UBWC, 2), + SDE_FETCH_UBWC, 2, SDE_TILE_HEIGHT_UBWC), - PSEUDO_YUV_FMT(NV12, + PSEUDO_YUV_FMT_TILED(NV12, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C1_B_Cb, C2_R_Cr, SDE_CHROMA_420, SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_COMPRESSED, - SDE_FETCH_UBWC, 4), + SDE_FETCH_UBWC, 4, SDE_TILE_HEIGHT_NV12), }; static const struct sde_format sde_format_map_p010[] = { @@ -539,21 +632,21 @@ static const struct sde_format sde_format_map_p010[] = { }; static const struct sde_format sde_format_map_p010_ubwc[] = { - PSEUDO_YUV_FMT_LOOSE(NV12, + PSEUDO_YUV_FMT_LOOSE_TILED(NV12, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C1_B_Cb, C2_R_Cr, SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX | SDE_FORMAT_FLAG_COMPRESSED), - SDE_FETCH_UBWC, 4), + SDE_FETCH_UBWC, 4, SDE_TILE_HEIGHT_NV12), }; static const struct sde_format sde_format_map_tp10_ubwc[] = { - PSEUDO_YUV_FMT(NV12, + PSEUDO_YUV_FMT_TILED(NV12, 0, COLOR_8BIT, COLOR_8BIT, COLOR_8BIT, C1_B_Cb, C2_R_Cr, SDE_CHROMA_420, (SDE_FORMAT_FLAG_YUV | SDE_FORMAT_FLAG_DX | SDE_FORMAT_FLAG_COMPRESSED), - SDE_FETCH_UBWC, 4), + SDE_FETCH_UBWC, 4, SDE_TILE_HEIGHT_NV12), }; /* _sde_get_v_h_subsample_rate - Get subsample rates for all formats we support diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c index 9c572ce26166..ed9a6ea37397 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c @@ -719,7 +719,8 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg, sblk->maxdwnscale = MAX_SSPP_DOWNSCALE; sblk->format_list = plane_formats_yuv; sspp->id = SSPP_VIG0 + *vig_count; - snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u", sspp->id); + snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u", + sspp->id - SSPP_VIG0); sspp->clk_ctrl = SDE_CLK_CTRL_VIG0 + *vig_count; sspp->type = SSPP_TYPE_VIG; set_bit(SDE_SSPP_QOS, &sspp->features); @@ -736,7 +737,7 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg, sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value, VIG_QSEED_LEN, 0); snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN, - "sspp_scaler%u", sspp->id); + "sspp_scaler%u", sspp->id - SSPP_VIG0); } else if (sde_cfg->qseed_type == SDE_SSPP_SCALER_QSEED3) { set_bit(SDE_SSPP_SCALER_QSEED3, &sspp->features); sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED3; @@ -745,12 +746,12 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg, sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value, VIG_QSEED_LEN, 0); snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN, - "sspp_scaler%u", sspp->id); + "sspp_scaler%u", sspp->id - SSPP_VIG0); } sblk->csc_blk.id = SDE_SSPP_CSC; snprintf(sblk->csc_blk.name, SDE_HW_BLK_NAME_LEN, - "sspp_csc%u", sspp->id); + "sspp_csc%u", sspp->id - SSPP_VIG0); if (sde_cfg->csc_type == SDE_SSPP_CSC) { set_bit(SDE_SSPP_CSC, &sspp->features); sblk->csc_blk.base = PROP_VALUE_ACCESS(prop_value, @@ -763,7 +764,7 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg, sblk->hsic_blk.id = SDE_SSPP_HSIC; snprintf(sblk->hsic_blk.name, SDE_HW_BLK_NAME_LEN, - "sspp_hsic%u", sspp->id); + "sspp_hsic%u", sspp->id - SSPP_VIG0); if (prop_exists[VIG_HSIC_PROP]) { sblk->hsic_blk.base = PROP_VALUE_ACCESS(prop_value, VIG_HSIC_PROP, 0); @@ -775,7 +776,7 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg, sblk->memcolor_blk.id = SDE_SSPP_MEMCOLOR; snprintf(sblk->memcolor_blk.name, SDE_HW_BLK_NAME_LEN, - "sspp_memcolor%u", sspp->id); + "sspp_memcolor%u", sspp->id - SSPP_VIG0); if (prop_exists[VIG_MEMCOLOR_PROP]) { sblk->memcolor_blk.base = PROP_VALUE_ACCESS(prop_value, VIG_MEMCOLOR_PROP, 0); @@ -787,7 +788,7 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg, sblk->pcc_blk.id = SDE_SSPP_PCC; snprintf(sblk->pcc_blk.name, SDE_HW_BLK_NAME_LEN, - "sspp_pcc%u", sspp->id); + "sspp_pcc%u", sspp->id - SSPP_VIG0); if (prop_exists[VIG_PCC_PROP]) { sblk->pcc_blk.base = PROP_VALUE_ACCESS(prop_value, VIG_PCC_PROP, 0); @@ -807,7 +808,8 @@ static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg, sblk->maxdwnscale = MAX_SSPP_DOWNSCALE; sblk->format_list = plane_formats; sspp->id = SSPP_RGB0 + *rgb_count; - snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u", sspp->id); + snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u", + sspp->id - SSPP_VIG0); sspp->clk_ctrl = SDE_CLK_CTRL_RGB0 + *rgb_count; sspp->type = SSPP_TYPE_RGB; set_bit(SDE_SSPP_QOS, &sspp->features); @@ -824,7 +826,7 @@ static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg, sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value, RGB_SCALER_LEN, 0); snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN, - "sspp_scaler%u", sspp->id); + "sspp_scaler%u", sspp->id - SSPP_VIG0); } else if (sde_cfg->qseed_type == SDE_SSPP_SCALER_QSEED3) { set_bit(SDE_SSPP_SCALER_RGB, &sspp->features); sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED3; @@ -833,7 +835,7 @@ static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg, sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value, SSPP_SCALE_SIZE, 0); snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN, - "sspp_scaler%u", sspp->id); + "sspp_scaler%u", sspp->id - SSPP_VIG0); } sblk->pcc_blk.id = SDE_SSPP_PCC; @@ -857,7 +859,8 @@ static void _sde_sspp_setup_cursor(struct sde_mdss_cfg *sde_cfg, sblk->maxdwnscale = SSPP_UNITY_SCALE; sblk->format_list = cursor_formats; sspp->id = SSPP_CURSOR0 + *cursor_count; - snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u", sspp->id); + snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u", + sspp->id - SSPP_VIG0); sspp->clk_ctrl = SDE_CLK_CTRL_CURSOR0 + *cursor_count; sspp->type = SSPP_TYPE_CURSOR; (*cursor_count)++; @@ -874,7 +877,8 @@ static void _sde_sspp_setup_dma(struct sde_mdss_cfg *sde_cfg, sspp->id = SSPP_DMA0 + *dma_count; sspp->clk_ctrl = SDE_CLK_CTRL_DMA0 + *dma_count; sspp->type = SSPP_TYPE_DMA; - snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u", sspp->id); + snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u", + sspp->id - SSPP_VIG0); set_bit(SDE_SSPP_QOS, &sspp->features); (*dma_count)++; snprintf(sspp->name, sizeof(sspp->name), "dma%d", *dma_count-1); @@ -978,8 +982,6 @@ static int sde_sspp_parse_dt(struct device_node *np, set_bit(SDE_SSPP_SRC, &sspp->features); sblk->src_blk.id = SDE_SSPP_SRC; - snprintf(sblk->src_blk.name, SDE_HW_BLK_NAME_LEN, "sspp_src_%u", - sblk->src_blk.id); of_property_read_string_index(np, sspp_prop[SSPP_TYPE].prop_name, i, &type); @@ -1104,7 +1106,8 @@ static int sde_ctl_parse_dt(struct device_node *np, ctl->base = PROP_VALUE_ACCESS(prop_value, HW_OFF, i); ctl->len = PROP_VALUE_ACCESS(prop_value, HW_LEN, 0); ctl->id = CTL_0 + i; - snprintf(ctl->name, SDE_HW_BLK_NAME_LEN, "ctl_%u", ctl->id); + snprintf(ctl->name, SDE_HW_BLK_NAME_LEN, "ctl_%u", + ctl->id - CTL_0); if (i < MAX_SPLIT_DISPLAY_CTL) set_bit(SDE_CTL_SPLIT_DISPLAY, &ctl->features); @@ -1196,7 +1199,8 @@ static int sde_mixer_parse_dt(struct device_node *np, mixer->base = PROP_VALUE_ACCESS(prop_value, MIXER_OFF, i); mixer->len = PROP_VALUE_ACCESS(prop_value, MIXER_LEN, 0); mixer->id = LM_0 + i; - snprintf(mixer->name, SDE_HW_BLK_NAME_LEN, "lm_%u", mixer->id); + snprintf(mixer->name, SDE_HW_BLK_NAME_LEN, "lm_%u", + mixer->id - LM_0); if (!prop_exists[MIXER_LEN]) mixer->len = DEFAULT_SDE_HW_BLOCK_LEN; @@ -1284,7 +1288,8 @@ static int sde_intf_parse_dt(struct device_node *np, intf->base = PROP_VALUE_ACCESS(prop_value, INTF_OFF, i); intf->len = PROP_VALUE_ACCESS(prop_value, INTF_LEN, 0); intf->id = INTF_0 + i; - snprintf(intf->name, SDE_HW_BLK_NAME_LEN, "intf_%u", intf->id); + snprintf(intf->name, SDE_HW_BLK_NAME_LEN, "intf_%u", + intf->id - INTF_0); if (!prop_exists[INTF_LEN]) intf->len = DEFAULT_SDE_HW_BLOCK_LEN; @@ -1365,7 +1370,8 @@ static int sde_wb_parse_dt(struct device_node *np, wb->base = PROP_VALUE_ACCESS(prop_value, WB_OFF, i); wb->id = WB_0 + PROP_VALUE_ACCESS(prop_value, WB_ID, i); - snprintf(wb->name, SDE_HW_BLK_NAME_LEN, "wb_%u", wb->id); + snprintf(wb->name, SDE_HW_BLK_NAME_LEN, "wb_%u", + wb->id - WB_0); wb->clk_ctrl = SDE_CLK_CTRL_WB0 + PROP_VALUE_ACCESS(prop_value, WB_ID, i); wb->xin_id = PROP_VALUE_ACCESS(prop_value, WB_XIN_ID, i); @@ -1601,7 +1607,8 @@ static int sde_dspp_parse_dt(struct device_node *np, dspp->base = PROP_VALUE_ACCESS(prop_value, DSPP_OFF, i); dspp->len = PROP_VALUE_ACCESS(prop_value, DSPP_SIZE, 0); dspp->id = DSPP_0 + i; - snprintf(dspp->name, SDE_HW_BLK_NAME_LEN, "dspp_%u", dspp->id); + snprintf(dspp->name, SDE_HW_BLK_NAME_LEN, "dspp_%u", + dspp->id - DSPP_0); sblk = kzalloc(sizeof(*sblk), GFP_KERNEL); if (!sblk) { @@ -1671,7 +1678,8 @@ static int sde_cdm_parse_dt(struct device_node *np, cdm = sde_cfg->cdm + i; cdm->base = PROP_VALUE_ACCESS(prop_value, HW_OFF, i); cdm->id = CDM_0 + i; - snprintf(cdm->name, SDE_HW_BLK_NAME_LEN, "cdm_%u", cdm->id); + snprintf(cdm->name, SDE_HW_BLK_NAME_LEN, "cdm_%u", + cdm->id - CDM_0); cdm->len = PROP_VALUE_ACCESS(prop_value, HW_LEN, 0); /* intf3 and wb2 for cdm block */ @@ -1737,6 +1745,8 @@ static int sde_vbif_parse_dt(struct device_node *np, vbif->base = PROP_VALUE_ACCESS(prop_value, VBIF_OFF, i); vbif->len = vbif_len; vbif->id = VBIF_0 + PROP_VALUE_ACCESS(prop_value, VBIF_ID, i); + snprintf(vbif->name, SDE_HW_BLK_NAME_LEN, "vbif_%u", + vbif->id - VBIF_0); SDE_DEBUG("vbif:%d\n", vbif->id - VBIF_0); @@ -1864,19 +1874,21 @@ static int sde_pp_parse_dt(struct device_node *np, pp->base = PROP_VALUE_ACCESS(prop_value, PP_OFF, i); pp->id = PINGPONG_0 + i; - snprintf(pp->name, SDE_HW_BLK_NAME_LEN, "pingpong_%u", pp->id); + snprintf(pp->name, SDE_HW_BLK_NAME_LEN, "pingpong_%u", + pp->id - PINGPONG_0); pp->len = PROP_VALUE_ACCESS(prop_value, PP_LEN, 0); sblk->te.base = PROP_VALUE_ACCESS(prop_value, TE_OFF, i); sblk->te.id = SDE_PINGPONG_TE; - snprintf(sblk->te.name, SDE_HW_BLK_NAME_LEN, "te_%u", pp->id); + snprintf(sblk->te.name, SDE_HW_BLK_NAME_LEN, "te_%u", + pp->id - PINGPONG_0); set_bit(SDE_PINGPONG_TE, &pp->features); sblk->te2.base = PROP_VALUE_ACCESS(prop_value, TE2_OFF, i); if (sblk->te2.base) { sblk->te2.id = SDE_PINGPONG_TE2; snprintf(sblk->te2.name, SDE_HW_BLK_NAME_LEN, "te2_%u", - pp->id); + pp->id - PINGPONG_0); set_bit(SDE_PINGPONG_TE2, &pp->features); set_bit(SDE_PINGPONG_SPLIT, &pp->features); } @@ -1888,7 +1900,7 @@ static int sde_pp_parse_dt(struct device_node *np, if (sblk->dsc.base) { sblk->dsc.id = SDE_PINGPONG_DSC; snprintf(sblk->dsc.name, SDE_HW_BLK_NAME_LEN, "dsc_%u", - pp->id); + sblk->dsc.id - PINGPONG_0); set_bit(SDE_PINGPONG_DSC, &pp->features); } } @@ -2020,12 +2032,12 @@ static int sde_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg) cfg->mdss[0].base = MDSS_BASE_OFFSET; cfg->mdss[0].id = MDP_TOP; snprintf(cfg->mdss[0].name, SDE_HW_BLK_NAME_LEN, "mdss_%u", - cfg->mdss[0].id); + cfg->mdss[0].id - MDP_TOP); cfg->mdp_count = 1; cfg->mdp[0].id = MDP_TOP; snprintf(cfg->mdp[0].name, SDE_HW_BLK_NAME_LEN, "top_%u", - cfg->mdp[0].id); + cfg->mdp[0].id - MDP_TOP); cfg->mdp[0].base = PROP_VALUE_ACCESS(prop_value, SDE_OFF, 0); cfg->mdp[0].len = PROP_VALUE_ACCESS(prop_value, SDE_LEN, 0); if (!prop_exists[SDE_LEN]) diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h index 9d9a18884938..81e6bfe6defe 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h @@ -44,7 +44,7 @@ #define SDE_HW_VER_172 SDE_HW_VER(1, 7, 2) /* 8996 v3.0 */ #define SDE_HW_VER_300 SDE_HW_VER(3, 0, 0) /* 8998 v1.0 */ #define SDE_HW_VER_301 SDE_HW_VER(3, 0, 1) /* 8998 v1.1 */ -#define SDE_HW_VER_400 SDE_HW_VER(4, 0, 0) /* msmskunk v1.0 */ +#define SDE_HW_VER_400 SDE_HW_VER(4, 0, 0) /* sdm845 v1.0 */ #define IS_MSMSKUNK_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_400) diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c index 5f0a8340aabe..f386840bfaf5 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.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 @@ -14,6 +14,7 @@ #include "sde_hwio.h" #include "sde_hw_catalog.h" #include "sde_hw_cdm.h" +#include "sde_dbg.h" #define CDM_CSC_10_OPMODE 0x000 #define CDM_CSC_10_BASE 0x004 @@ -296,6 +297,9 @@ struct sde_hw_cdm *sde_hw_cdm_init(enum sde_cdm idx, */ sde_hw_cdm_setup_csc_10bit(c, &rgb2yuv_cfg); + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off, + c->hw.blk_off + c->hw.length, c->hw.xin_id); + return c; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c index f1f66f37ba6a..451074ea3b53 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, 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 @@ -326,7 +326,7 @@ void sde_setup_dspp_pcc_v1_7(struct sde_hw_dspp *ctx, void *cfg) void __iomem *base; if (!hw_cfg || (hw_cfg->len != sizeof(*pcc) && hw_cfg->payload)) { - DRM_ERROR("invalid params hw %p payload %p payloadsize %d \"\ + DRM_ERROR("invalid params hw %pK payload %pK payloadsize %d \"\ exp size %zd\n", hw_cfg, ((hw_cfg) ? hw_cfg->payload : NULL), ((hw_cfg) ? hw_cfg->len : 0), sizeof(*pcc)); diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c index 6ddf0f3b06c8..81a62f06d608 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.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 @@ -13,6 +13,7 @@ #include #include "sde_hwio.h" #include "sde_hw_ctl.h" +#include "sde_dbg.h" #define CTL_LAYER(lm) \ (((lm) == LM_5) ? (0x024) : (((lm) - LM_0) * 0x004)) @@ -20,6 +21,9 @@ (0x40 + (((lm) - LM_0) * 0x004)) #define CTL_LAYER_EXT2(lm) \ (0x70 + (((lm) - LM_0) * 0x004)) +#define CTL_LAYER_EXT3(lm) \ + (0xA0 + (((lm) - LM_0) * 0x004)) + #define CTL_TOP 0x014 #define CTL_FLUSH 0x018 #define CTL_START 0x01C @@ -93,6 +97,12 @@ static inline void sde_hw_ctl_trigger_flush(struct sde_hw_ctl *ctx) SDE_REG_WRITE(&ctx->hw, CTL_FLUSH, ctx->pending_flush_mask); } +static inline u32 sde_hw_ctl_get_flush_register(struct sde_hw_ctl *ctx) +{ + struct sde_hw_blk_reg_map *c = &ctx->hw; + + return SDE_REG_READ(c, CTL_FLUSH); +} static inline uint32_t sde_hw_ctl_get_bitmask_sspp(struct sde_hw_ctl *ctx, enum sde_sspp sspp) @@ -308,8 +318,12 @@ static void sde_hw_ctl_clear_all_blendstages(struct sde_hw_ctl *ctx) int i; for (i = 0; i < ctx->mixer_count; i++) { - SDE_REG_WRITE(c, CTL_LAYER(LM_0 + i), 0); - SDE_REG_WRITE(c, CTL_LAYER_EXT(LM_0 + i), 0); + int mixer_id = ctx->mixer_hw_caps[i].id; + + SDE_REG_WRITE(c, CTL_LAYER(mixer_id), 0); + SDE_REG_WRITE(c, CTL_LAYER_EXT(mixer_id), 0); + SDE_REG_WRITE(c, CTL_LAYER_EXT2(mixer_id), 0); + SDE_REG_WRITE(c, CTL_LAYER_EXT3(mixer_id), 0); } } @@ -451,6 +465,7 @@ static void _setup_ctl_ops(struct sde_hw_ctl_ops *ops, ops->update_pending_flush = sde_hw_ctl_update_pending_flush; ops->get_pending_flush = sde_hw_ctl_get_pending_flush; ops->trigger_flush = sde_hw_ctl_trigger_flush; + ops->get_flush_register = sde_hw_ctl_get_flush_register; ops->trigger_start = sde_hw_ctl_trigger_start; ops->setup_intf_cfg = sde_hw_ctl_intf_cfg; ops->reset = sde_hw_ctl_reset_control; @@ -489,6 +504,9 @@ struct sde_hw_ctl *sde_hw_ctl_init(enum sde_ctl idx, c->mixer_count = m->mixer_count; c->mixer_hw_caps = m->mixer; + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off, + c->hw.blk_off + c->hw.length, c->hw.xin_id); + return c; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h index 2b54912460ae..74dbde92639a 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, 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 @@ -93,6 +93,13 @@ struct sde_hw_ctl_ops { */ void (*trigger_flush)(struct sde_hw_ctl *ctx); + /** + * Read the value of the flush register + * @ctx : ctl path ctx pointer + * @Return: value of the ctl flush register. + */ + u32 (*get_flush_register)(struct sde_hw_ctl *ctx); + /** * Setup ctl_path interface config * @ctx diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c index bcf19a0036b5..3250e5a75905 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.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 @@ -15,6 +15,7 @@ #include "sde_hw_catalog.h" #include "sde_hw_dspp.h" #include "sde_hw_color_processing.h" +#include "sde_dbg.h" static struct sde_dspp_cfg *_dspp_offset(enum sde_dspp dspp, struct sde_mdss_cfg *m, @@ -112,6 +113,9 @@ struct sde_hw_dspp *sde_hw_dspp_init(enum sde_dspp idx, c->cap = cfg; _setup_dspp_ops(c, c->cap->features); + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off, + c->hw.blk_off + c->hw.length, c->hw.xin_id); + return c; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_intf.c b/drivers/gpu/drm/msm/sde/sde_hw_intf.c index bfa5f3e2a682..9d868297c057 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_intf.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_intf.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 @@ -13,6 +13,7 @@ #include "sde_hwio.h" #include "sde_hw_catalog.h" #include "sde_hw_intf.h" +#include "sde_dbg.h" #define INTF_TIMING_ENGINE_EN 0x000 #define INTF_CONFIG 0x004 @@ -325,9 +326,9 @@ struct sde_hw_intf *sde_hw_intf_init(enum sde_intf idx, c->mdss = m; _setup_intf_ops(&c->ops, c->cap->features); - /* - * Perform any default initialization for the intf - */ + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off, + c->hw.blk_off + c->hw.length, c->hw.xin_id); + return c; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_lm.c b/drivers/gpu/drm/msm/sde/sde_hw_lm.c index 0badd5fe1eae..40f877dd0811 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_lm.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_lm.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 @@ -14,6 +14,7 @@ #include "sde_hwio.h" #include "sde_hw_lm.h" #include "sde_hw_mdss.h" +#include "sde_dbg.h" #define LM_OP_MODE 0x00 #define LM_OUT_SIZE 0x04 @@ -196,9 +197,9 @@ struct sde_hw_mixer *sde_hw_lm_init(enum sde_lm idx, c->cap = cfg; _setup_mixer_ops(m, &c->ops, c->cap->features); - /* - * Perform any default initialization for the sspp blocks - */ + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off, + c->hw.blk_off + c->hw.length, c->hw.xin_id); + return c; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h index 92dd829eee3e..05c876e3b685 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h +++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.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 @@ -18,6 +18,8 @@ #include "msm_drv.h" +#define SDE_DBG_NAME "sde" + #define SDE_NONE 0 #ifndef SDE_CSC_MATRIX_COEFF_SIZE diff --git a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c index 7f5f2c3d0ae0..67dccedd05a2 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.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 @@ -14,6 +14,7 @@ #include "sde_hwio.h" #include "sde_hw_catalog.h" #include "sde_hw_pingpong.h" +#include "sde_dbg.h" #define PP_TEAR_CHECK_EN 0x000 #define PP_SYNC_CONFIG_VSYNC 0x004 @@ -160,6 +161,9 @@ struct sde_hw_pingpong *sde_hw_pingpong_init(enum sde_pingpong idx, c->pingpong_hw_cap = cfg; _setup_pingpong_ops(&c->ops, c->pingpong_hw_cap->features); + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off, + c->hw.blk_off + c->hw.length, c->hw.xin_id); + return c; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c index 7bc624eaf80e..72235725d5a2 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.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 @@ -15,6 +15,7 @@ #include "sde_hw_lm.h" #include "sde_hw_sspp.h" #include "sde_hw_color_processing.h" +#include "sde_dbg.h" #define SDE_FETCH_CONFIG_RESET_VALUE 0x00000087 @@ -594,10 +595,8 @@ static void _sde_hw_sspp_setup_scaler3(struct sde_hw_pipe *ctx, || !scaler3_cfg || !ctx || !ctx->cap || !ctx->cap->sblk) return; - if (!scaler3_cfg->enable) { - SDE_REG_WRITE(&ctx->hw, QSEED3_OP_MODE + idx, 0x0); - return; - } + if (!scaler3_cfg->enable) + goto end; op_mode |= BIT(0); op_mode |= (scaler3_cfg->y_rgb_filter_cfg & 0x3) << 16; @@ -607,9 +606,6 @@ static void _sde_hw_sspp_setup_scaler3(struct sde_hw_pipe *ctx, op_mode |= (scaler3_cfg->uv_filter_cfg & 0x3) << 24; } - if (!SDE_FORMAT_IS_DX(sspp->layout.format)) - op_mode |= BIT(14); - op_mode |= (scaler3_cfg->blend_cfg & 1) << 31; op_mode |= (scaler3_cfg->dir_en) ? BIT(4) : 0; @@ -637,10 +633,6 @@ static void _sde_hw_sspp_setup_scaler3(struct sde_hw_pipe *ctx, _sde_hw_sspp_setup_scaler3_lut(ctx, scaler3_cfg); if (ctx->cap->sblk->scaler_blk.version == 0x1002) { - if (sspp->layout.format->alpha_enable) { - op_mode |= BIT(10); - op_mode |= (scaler3_cfg->alpha_filter_cfg & 0x1) << 30; - } phase_init = ((scaler3_cfg->init_phase_x[0] & 0x3F) << 0) | ((scaler3_cfg->init_phase_y[0] & 0x3F) << 8) | @@ -648,10 +640,6 @@ static void _sde_hw_sspp_setup_scaler3(struct sde_hw_pipe *ctx, ((scaler3_cfg->init_phase_y[1] & 0x3F) << 24); SDE_REG_WRITE(&ctx->hw, QSEED3_PHASE_INIT + idx, phase_init); } else { - if (sspp->layout.format->alpha_enable) { - op_mode |= BIT(10); - op_mode |= (scaler3_cfg->alpha_filter_cfg & 0x3) << 29; - } SDE_REG_WRITE(&ctx->hw, QSEED3_PHASE_INIT_Y_H + idx, scaler3_cfg->init_phase_x[0] & 0x1FFFFF); SDE_REG_WRITE(&ctx->hw, QSEED3_PHASE_INIT_Y_V + idx, @@ -682,6 +670,17 @@ static void _sde_hw_sspp_setup_scaler3(struct sde_hw_pipe *ctx, SDE_REG_WRITE(&ctx->hw, QSEED3_DST_SIZE + idx, dst); +end: + if (!SDE_FORMAT_IS_DX(sspp->layout.format)) + op_mode |= BIT(14); + + if (sspp->layout.format->alpha_enable) { + op_mode |= BIT(10); + if (ctx->cap->sblk->scaler_blk.version == 0x1002) + op_mode |= (scaler3_cfg->alpha_filter_cfg & 0x1) << 30; + else + op_mode |= (scaler3_cfg->alpha_filter_cfg & 0x3) << 29; + } SDE_REG_WRITE(&ctx->hw, QSEED3_OP_MODE + idx, op_mode); } @@ -937,6 +936,19 @@ struct sde_hw_pipe *sde_hw_sspp_init(enum sde_sspp idx, _setup_layer_ops(hw_pipe, hw_pipe->cap->features); hw_pipe->highest_bank_bit = catalog->mdp[0].highest_bank_bit; + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, + hw_pipe->hw.blk_off, + hw_pipe->hw.blk_off + hw_pipe->hw.length, + hw_pipe->hw.xin_id); + + if (cfg->sblk->scaler_blk.len) + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, + cfg->sblk->scaler_blk.name, + hw_pipe->hw.blk_off + cfg->sblk->scaler_blk.base, + hw_pipe->hw.blk_off + cfg->sblk->scaler_blk.base + + cfg->sblk->scaler_blk.len, + hw_pipe->hw.xin_id); + return hw_pipe; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.c b/drivers/gpu/drm/msm/sde/sde_hw_top.c index c9759496c4d5..0cc4cc6752cf 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_top.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_top.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 @@ -13,6 +13,7 @@ #include "sde_hwio.h" #include "sde_hw_catalog.h" #include "sde_hw_top.h" +#include "sde_dbg.h" #define SSPP_SPARE 0x28 @@ -259,9 +260,10 @@ struct sde_hw_mdp *sde_hw_mdptop_init(enum sde_mdp idx, mdp->cap = cfg; _setup_mdp_ops(&mdp->ops, mdp->cap->features); - /* - * Perform any default initialization for the intf - */ + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, + mdp->hw.blk_off, mdp->hw.blk_off + mdp->hw.length, + mdp->hw.xin_id); + sde_dbg_set_sde_top_offset(mdp->hw.blk_off); return mdp; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c index c548b21d7d66..55e78c31471f 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_vbif.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 @@ -13,6 +13,7 @@ #include "sde_hwio.h" #include "sde_hw_catalog.h" #include "sde_hw_vbif.h" +#include "sde_dbg.h" #define VBIF_VERSION 0x0000 #define VBIF_CLK_FORCE_CTRL0 0x0008 @@ -157,6 +158,8 @@ struct sde_hw_vbif *sde_hw_vbif_init(enum sde_vbif idx, c->cap = cfg; _setup_vbif_ops(&c->ops, c->cap->features); + /* no need to register sub-range in sde dbg, dump entire vbif io base */ + return c; } diff --git a/drivers/gpu/drm/msm/sde/sde_hw_wb.c b/drivers/gpu/drm/msm/sde/sde_hw_wb.c index afca7152db08..9a44f7215a5f 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_wb.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_wb.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 @@ -15,6 +15,7 @@ #include "sde_hw_catalog.h" #include "sde_hw_wb.h" #include "sde_formats.h" +#include "sde_dbg.h" #define WB_DST_FORMAT 0x000 #define WB_DST_OP_MODE 0x004 @@ -216,6 +217,9 @@ struct sde_hw_wb *sde_hw_wb_init(enum sde_wb idx, c->highest_bank_bit = m->mdp[0].highest_bank_bit; c->hw_mdp = hw_mdp; + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off, + c->hw.blk_off + c->hw.length, c->hw.xin_id); + return c; } diff --git a/drivers/gpu/drm/msm/sde/sde_irq.c b/drivers/gpu/drm/msm/sde/sde_irq.c index eeb7a0002eab..7864b9fef87b 100644 --- a/drivers/gpu/drm/msm/sde/sde_irq.c +++ b/drivers/gpu/drm/msm/sde/sde_irq.c @@ -19,6 +19,8 @@ #include "sde_irq.h" #include "sde_core_irq.h" +static uint32_t g_sde_irq_status; + irqreturn_t sde_irq(struct msm_kms *kms) { struct sde_kms *sde_kms = to_sde_kms(kms); @@ -27,6 +29,9 @@ irqreturn_t sde_irq(struct msm_kms *kms) sde_kms->hw_intr->ops.get_interrupt_sources(sde_kms->hw_intr, &interrupts); + /* store irq status in case of irq-storm debugging */ + g_sde_irq_status = interrupts; + /* * Taking care of MDP interrupt */ @@ -40,13 +45,30 @@ irqreturn_t sde_irq(struct msm_kms *kms) */ while (interrupts) { irq_hw_number_t hwirq = fls(interrupts) - 1; + unsigned int mapping; + int rc; + + mapping = irq_find_mapping(sde_kms->irq_controller.domain, + hwirq); + if (mapping == 0) { + SDE_EVT32(hwirq, SDE_EVTLOG_ERROR); + goto error; + } + + rc = generic_handle_irq(mapping); + if (rc < 0) { + SDE_EVT32(hwirq, mapping, rc, SDE_EVTLOG_ERROR); + goto error; + } - generic_handle_irq(irq_find_mapping( - sde_kms->irq_controller.domain, hwirq)); interrupts &= ~(1 << hwirq); } return IRQ_HANDLED; + +error: + /* bad situation, inform irq system, it may disable overall MDSS irq */ + return IRQ_NONE; } void sde_irq_preinstall(struct msm_kms *kms) diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c index cdf67c0aa864..44e7c1ad8c08 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.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 * @@ -1167,6 +1167,44 @@ static int _sde_kms_mmu_init(struct sde_kms *sde_kms) return ret; } +static void __iomem *_sde_kms_ioremap(struct platform_device *pdev, + const char *name, unsigned long *out_size) +{ + struct resource *res; + unsigned long size; + void __iomem *ptr; + + if (out_size) + *out_size = 0; + + if (name) + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + else + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res) { + /* availability depends on platform */ + SDE_DEBUG("failed to get memory resource: %s\n", name); + return NULL; + } + + size = resource_size(res); + + ptr = devm_ioremap_nocache(&pdev->dev, res->start, size); + if (!ptr) { + SDE_ERROR("failed to ioremap: %s\n", name); + return NULL; + } + + SDE_DEBUG("IO:region %s %pK %08lx\n", name, ptr, size); + + if (out_size) + *out_size = size; + + return ptr; +} + + static int sde_kms_hw_init(struct msm_kms *kms) { struct sde_kms *sde_kms; @@ -1193,29 +1231,42 @@ static int sde_kms_hw_init(struct msm_kms *kms) goto end; } - sde_kms->mmio = msm_ioremap(dev->platformdev, "mdp_phys", "SDE"); - if (IS_ERR(sde_kms->mmio)) { - rc = PTR_ERR(sde_kms->mmio); - SDE_ERROR("mdp register memory map failed: %d\n", rc); - sde_kms->mmio = NULL; + sde_kms->mmio = _sde_kms_ioremap(dev->platformdev, "mdp_phys", + &sde_kms->mmio_len); + if (!sde_kms->mmio) { + SDE_ERROR("mdp register memory map failed\n"); goto error; } - DRM_INFO("mapped mdp address space @%p\n", sde_kms->mmio); - - sde_kms->vbif[VBIF_RT] = msm_ioremap(dev->platformdev, - "vbif_phys", "VBIF"); - if (IS_ERR(sde_kms->vbif[VBIF_RT])) { - rc = PTR_ERR(sde_kms->vbif[VBIF_RT]); - SDE_ERROR("vbif register memory map failed: %d\n", rc); - sde_kms->vbif[VBIF_RT] = NULL; + DRM_INFO("mapped mdp address space @%pK\n", sde_kms->mmio); + + rc = sde_dbg_reg_register_base(SDE_DBG_NAME, sde_kms->mmio, + sde_kms->mmio_len); + if (rc) + SDE_ERROR("dbg base register kms failed: %d\n", rc); + + sde_kms->vbif[VBIF_RT] = _sde_kms_ioremap(dev->platformdev, "vbif_phys", + &sde_kms->vbif_len[VBIF_RT]); + if (!sde_kms->vbif[VBIF_RT]) { + SDE_ERROR("vbif register memory map failed\n"); goto error; } - sde_kms->vbif[VBIF_NRT] = msm_ioremap(dev->platformdev, - "vbif_nrt_phys", "VBIF_NRT"); - if (IS_ERR(sde_kms->vbif[VBIF_NRT])) { - sde_kms->vbif[VBIF_NRT] = NULL; + rc = sde_dbg_reg_register_base("vbif_rt", sde_kms->vbif[VBIF_RT], + sde_kms->vbif_len[VBIF_RT]); + if (rc) + SDE_ERROR("dbg base register vbif_rt failed: %d\n", rc); + + sde_kms->vbif[VBIF_NRT] = _sde_kms_ioremap(dev->platformdev, + "vbif_nrt_phys", &sde_kms->vbif_len[VBIF_NRT]); + if (!sde_kms->vbif[VBIF_NRT]) { SDE_DEBUG("VBIF NRT is not defined"); + } else { + rc = sde_dbg_reg_register_base("vbif_nrt", + sde_kms->vbif[VBIF_NRT], + sde_kms->vbif_len[VBIF_NRT]); + if (rc) + SDE_ERROR("dbg base register vbif_nrt failed: %d\n", + rc); } sde_kms->core_client = sde_power_client_create(&priv->phandle, "core"); @@ -1245,6 +1296,8 @@ static int sde_kms_hw_init(struct msm_kms *kms) goto power_error; } + sde_dbg_init_dbg_buses(sde_kms->core_rev); + rc = sde_rm_init(&sde_kms->rm, sde_kms->catalog, sde_kms->mmio, sde_kms->dev); if (rc) { diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h index fa0288b89b91..961875d409b2 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.h +++ b/drivers/gpu/drm/msm/sde/sde_kms.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 * @@ -134,6 +134,7 @@ struct sde_kms { /* io/register spaces: */ void __iomem *mmio, *vbif[VBIF_MAX]; + unsigned long mmio_len, vbif_len[VBIF_MAX]; struct regulator *vdd; struct regulator *mmagic; @@ -369,6 +370,49 @@ void sde_kms_info_append_format(struct sde_kms_info *info, */ void sde_kms_info_stop(struct sde_kms_info *info); +/** + * sde_kms_rect_intersect - intersect two rectangles + * @r1: first rectangle + * @r2: scissor rectangle + * @result: result rectangle, all 0's on no intersection found + */ +void sde_kms_rect_intersect(const struct sde_rect *r1, + const struct sde_rect *r2, + struct sde_rect *result); + +/** + * sde_kms_rect_is_equal - compares two rects + * @r1: rect value to compare + * @r2: rect value to compare + * + * Returns 1 if the rects are same, 0 otherwise. + */ +static inline bool sde_kms_rect_is_equal(struct sde_rect *r1, + struct sde_rect *r2) +{ + if ((!r1 && r2) || (r1 && !r2)) + return false; + + if (!r1 && !r2) + return true; + + return r1->x == r2->x && r1->y == r2->y && r1->w == r2->w && + r1->h == r2->h; +} + +/** + * sde_kms_rect_is_null - returns true if the width or height of a rect is 0 + * @rect: rectangle to check for zero size + * @Return: True if width or height of rectangle is 0 + */ +static inline bool sde_kms_rect_is_null(const struct sde_rect *r) +{ + if (!r) + return true; + + return (!r->w || !r->h); +} + /** * Vblank enable/disable functions */ diff --git a/drivers/gpu/drm/msm/sde/sde_kms_utils.c b/drivers/gpu/drm/msm/sde/sde_kms_utils.c index 6e29c09deb40..30e12c969538 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms_utils.c +++ b/drivers/gpu/drm/msm/sde/sde_kms_utils.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2017, 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 @@ -151,3 +151,27 @@ void sde_kms_info_stop(struct sde_kms_info *info) info->len = info->staged_len + len; } } + +void sde_kms_rect_intersect(const struct sde_rect *r1, + const struct sde_rect *r2, + struct sde_rect *result) +{ + int l, t, r, b; + + if (!r1 || !r2 || !result) + return; + + l = max(r1->x, r2->x); + t = max(r1->y, r2->y); + r = min((r1->x + r1->w), (r2->x + r2->w)); + b = min((r1->y + r1->h), (r2->y + r2->h)); + + if (r < l || b < t) { + memset(result, 0, sizeof(*result)); + } else { + result->x = l; + result->y = t; + result->w = r - l; + result->h = b - t; + } +} diff --git a/drivers/gpu/drm/msm/sde/sde_trace.h b/drivers/gpu/drm/msm/sde/sde_trace.h index 2a4e6b59a08c..d28562eabccb 100644 --- a/drivers/gpu/drm/msm/sde/sde_trace.h +++ b/drivers/gpu/drm/msm/sde/sde_trace.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, 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 @@ -125,6 +125,22 @@ TRACE_EVENT(sde_cmd_release_bw, TP_printk("crtc:%d", __entry->crtc_id) ); +TRACE_EVENT(sde_encoder_underrun, + TP_PROTO(u32 enc_id, u32 underrun_cnt), + TP_ARGS(enc_id, underrun_cnt), + TP_STRUCT__entry( + __field(u32, enc_id) + __field(u32, underrun_cnt) + ), + TP_fast_assign( + __entry->enc_id = enc_id; + __entry->underrun_cnt = underrun_cnt; + + ), + TP_printk("enc:%d underrun_cnt:%d", __entry->enc_id, + __entry->underrun_cnt) +); + TRACE_EVENT(sde_mark_write, TP_PROTO(int pid, const char *name, bool trace_begin), TP_ARGS(pid, name, trace_begin), diff --git a/drivers/gpu/drm/msm/sde_dbg.c b/drivers/gpu/drm/msm/sde_dbg.c new file mode 100644 index 000000000000..23d9abf981e0 --- /dev/null +++ b/drivers/gpu/drm/msm/sde_dbg.c @@ -0,0 +1,2288 @@ +/* Copyright (c) 2009-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) "[drm:%s:%d] " fmt, __func__, __LINE__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sde_dbg.h" +#include "sde/sde_hw_catalog.h" + +#define SDE_DBG_BASE_MAX 10 + +#define DEFAULT_PANIC 1 +#define DEFAULT_REGDUMP SDE_DBG_DUMP_IN_MEM +#define DEFAULT_DBGBUS_SDE SDE_DBG_DUMP_IN_MEM +#define DEFAULT_DBGBUS_VBIFRT SDE_DBG_DUMP_IN_MEM +#define DEFAULT_BASE_REG_CNT 0x100 +#define GROUP_BYTES 4 +#define ROW_BYTES 16 +#define RANGE_NAME_LEN 40 +#define REG_BASE_NAME_LEN 80 + +#define DBGBUS_FLAGS_DSPP BIT(0) +#define DBGBUS_DSPP_STATUS 0x34C + +#define DBGBUS_NAME_SDE "sde" +#define DBGBUS_NAME_VBIF_RT "vbif_rt" + +/* offsets from sde top address for the debug buses */ +#define DBGBUS_SSPP0 0x188 +#define DBGBUS_SSPP1 0x298 +#define DBGBUS_DSPP 0x348 +#define DBGBUS_PERIPH 0x418 + +#define TEST_MASK(id, tp) ((id << 4) | (tp << 1) | BIT(0)) + +/* following offsets are with respect to MDP VBIF base for DBG BUS access */ +#define MMSS_VBIF_CLKON 0x4 +#define MMSS_VBIF_TEST_BUS_OUT_CTRL 0x210 +#define MMSS_VBIF_TEST_BUS_OUT 0x230 + +/* print debug ranges in groups of 4 u32s */ +#define REG_DUMP_ALIGN 16 +#define DBG_CTRL_STOP_FTRACE BIT(0) +#define DBG_CTRL_PANIC_UNDERRUN BIT(1) +#define DBG_CTRL_MAX BIT(2) + +/** + * struct sde_dbg_reg_offset - tracking for start and end of region + * @start: start offset + * @start: end offset + */ +struct sde_dbg_reg_offset { + u32 start; + u32 end; +}; + +/** + * struct sde_dbg_reg_range - register dumping named sub-range + * @head: head of this node + * @reg_dump: address for the mem dump + * @range_name: name of this range + * @offset: offsets for range to dump + * @xin_id: client xin id + */ +struct sde_dbg_reg_range { + struct list_head head; + u32 *reg_dump; + char range_name[RANGE_NAME_LEN]; + struct sde_dbg_reg_offset offset; + uint32_t xin_id; +}; + +/** + * struct sde_dbg_reg_base - register region base. + * may sub-ranges: sub-ranges are used for dumping + * or may not have sub-ranges: dumping is base -> max_offset + * @reg_base_head: head of this node + * @sub_range_list: head to the list with dump ranges + * @name: register base name + * @base: base pointer + * @off: cached offset of region for manual register dumping + * @cnt: cached range of region for manual register dumping + * @max_offset: length of region + * @buf: buffer used for manual register dumping + * @buf_len: buffer length used for manual register dumping + * @reg_dump: address for the mem dump if no ranges used + */ +struct sde_dbg_reg_base { + struct list_head reg_base_head; + struct list_head sub_range_list; + char name[REG_BASE_NAME_LEN]; + void __iomem *base; + size_t off; + size_t cnt; + size_t max_offset; + char *buf; + size_t buf_len; + u32 *reg_dump; +}; + +struct sde_debug_bus_entry { + u32 wr_addr; + u32 block_id; + u32 test_id; +}; + +struct vbif_debug_bus_entry { + u32 disable_bus_addr; + u32 block_bus_addr; + u32 bit_offset; + u32 block_cnt; + u32 test_pnt_start; + u32 test_pnt_cnt; +}; + +struct sde_dbg_debug_bus_common { + char *name; + u32 enable_mask; + bool include_in_deferred_work; + u32 flags; + u32 entries_size; + u32 *dumped_content; +}; + +struct sde_dbg_sde_debug_bus { + struct sde_dbg_debug_bus_common cmn; + struct sde_debug_bus_entry *entries; + u32 top_blk_off; +}; + +struct sde_dbg_vbif_debug_bus { + struct sde_dbg_debug_bus_common cmn; + struct vbif_debug_bus_entry *entries; +}; + +/** + * struct sde_dbg_base - global sde debug base structure + * @evtlog: event log instance + * @reg_base_list: list of register dumping regions + * @root: base debugfs root + * @dev: device pointer + * @mutex: mutex to serialize access to serialze dumps, debugfs access + * @power_ctrl: callback structure for enabling power for reading hw registers + * @req_dump_blks: list of blocks requested for dumping + * @panic_on_err: whether to kernel panic after triggering dump via debugfs + * @dump_work: work struct for deferring register dump work to separate thread + * @work_panic: panic after dump if internal user passed "panic" special region + * @enable_reg_dump: whether to dump registers into memory, kernel log, or both + * @dbgbus_sde: debug bus structure for the sde + * @dbgbus_vbif_rt: debug bus structure for the realtime vbif + * @dump_all: dump all entries in register dump + */ +static struct sde_dbg_base { + struct sde_dbg_evtlog *evtlog; + struct list_head reg_base_list; + struct dentry *root; + struct device *dev; + struct mutex mutex; + struct sde_dbg_power_ctrl power_ctrl; + + struct sde_dbg_reg_base *req_dump_blks[SDE_DBG_BASE_MAX]; + + u32 panic_on_err; + struct work_struct dump_work; + bool work_panic; + u32 enable_reg_dump; + + struct sde_dbg_sde_debug_bus dbgbus_sde; + struct sde_dbg_vbif_debug_bus dbgbus_vbif_rt; + bool dump_all; + u32 debugfs_ctrl; +} sde_dbg_base; + +/* sde_dbg_base_evtlog - global pointer to main sde event log for macro use */ +struct sde_dbg_evtlog *sde_dbg_base_evtlog; + +static struct sde_debug_bus_entry dbg_bus_sde_8998[] = { + + /* Unpack 0 sspp 0*/ + { DBGBUS_SSPP0, 50, 2 }, + { DBGBUS_SSPP0, 60, 2 }, + { DBGBUS_SSPP0, 70, 2 }, + { DBGBUS_SSPP0, 85, 2 }, + + /* Upack 0 sspp 1*/ + { DBGBUS_SSPP1, 50, 2 }, + { DBGBUS_SSPP1, 60, 2 }, + { DBGBUS_SSPP1, 70, 2 }, + { DBGBUS_SSPP1, 85, 2 }, + + /* scheduler */ + { DBGBUS_DSPP, 130, 0 }, + { DBGBUS_DSPP, 130, 1 }, + { DBGBUS_DSPP, 130, 2 }, + { DBGBUS_DSPP, 130, 3 }, + { DBGBUS_DSPP, 130, 4 }, + { DBGBUS_DSPP, 130, 5 }, + + /* qseed */ + { DBGBUS_SSPP0, 6, 0}, + { DBGBUS_SSPP0, 6, 1}, + { DBGBUS_SSPP0, 26, 0}, + { DBGBUS_SSPP0, 26, 1}, + { DBGBUS_SSPP1, 6, 0}, + { DBGBUS_SSPP1, 6, 1}, + { DBGBUS_SSPP1, 26, 0}, + { DBGBUS_SSPP1, 26, 1}, + + /* scale */ + { DBGBUS_SSPP0, 16, 0}, + { DBGBUS_SSPP0, 16, 1}, + { DBGBUS_SSPP0, 36, 0}, + { DBGBUS_SSPP0, 36, 1}, + { DBGBUS_SSPP1, 16, 0}, + { DBGBUS_SSPP1, 16, 1}, + { DBGBUS_SSPP1, 36, 0}, + { DBGBUS_SSPP1, 36, 1}, + + /* fetch sspp0 */ + + /* vig 0 */ + { DBGBUS_SSPP0, 0, 0 }, + { DBGBUS_SSPP0, 0, 1 }, + { DBGBUS_SSPP0, 0, 2 }, + { DBGBUS_SSPP0, 0, 3 }, + { DBGBUS_SSPP0, 0, 4 }, + { DBGBUS_SSPP0, 0, 5 }, + { DBGBUS_SSPP0, 0, 6 }, + { DBGBUS_SSPP0, 0, 7 }, + + { DBGBUS_SSPP0, 1, 0 }, + { DBGBUS_SSPP0, 1, 1 }, + { DBGBUS_SSPP0, 1, 2 }, + { DBGBUS_SSPP0, 1, 3 }, + { DBGBUS_SSPP0, 1, 4 }, + { DBGBUS_SSPP0, 1, 5 }, + { DBGBUS_SSPP0, 1, 6 }, + { DBGBUS_SSPP0, 1, 7 }, + + { DBGBUS_SSPP0, 2, 0 }, + { DBGBUS_SSPP0, 2, 1 }, + { DBGBUS_SSPP0, 2, 2 }, + { DBGBUS_SSPP0, 2, 3 }, + { DBGBUS_SSPP0, 2, 4 }, + { DBGBUS_SSPP0, 2, 5 }, + { DBGBUS_SSPP0, 2, 6 }, + { DBGBUS_SSPP0, 2, 7 }, + + { DBGBUS_SSPP0, 4, 0 }, + { DBGBUS_SSPP0, 4, 1 }, + { DBGBUS_SSPP0, 4, 2 }, + { DBGBUS_SSPP0, 4, 3 }, + { DBGBUS_SSPP0, 4, 4 }, + { DBGBUS_SSPP0, 4, 5 }, + { DBGBUS_SSPP0, 4, 6 }, + { DBGBUS_SSPP0, 4, 7 }, + + { DBGBUS_SSPP0, 5, 0 }, + { DBGBUS_SSPP0, 5, 1 }, + { DBGBUS_SSPP0, 5, 2 }, + { DBGBUS_SSPP0, 5, 3 }, + { DBGBUS_SSPP0, 5, 4 }, + { DBGBUS_SSPP0, 5, 5 }, + { DBGBUS_SSPP0, 5, 6 }, + { DBGBUS_SSPP0, 5, 7 }, + + /* vig 2 */ + { DBGBUS_SSPP0, 20, 0 }, + { DBGBUS_SSPP0, 20, 1 }, + { DBGBUS_SSPP0, 20, 2 }, + { DBGBUS_SSPP0, 20, 3 }, + { DBGBUS_SSPP0, 20, 4 }, + { DBGBUS_SSPP0, 20, 5 }, + { DBGBUS_SSPP0, 20, 6 }, + { DBGBUS_SSPP0, 20, 7 }, + + { DBGBUS_SSPP0, 21, 0 }, + { DBGBUS_SSPP0, 21, 1 }, + { DBGBUS_SSPP0, 21, 2 }, + { DBGBUS_SSPP0, 21, 3 }, + { DBGBUS_SSPP0, 21, 4 }, + { DBGBUS_SSPP0, 21, 5 }, + { DBGBUS_SSPP0, 21, 6 }, + { DBGBUS_SSPP0, 21, 7 }, + + { DBGBUS_SSPP0, 22, 0 }, + { DBGBUS_SSPP0, 22, 1 }, + { DBGBUS_SSPP0, 22, 2 }, + { DBGBUS_SSPP0, 22, 3 }, + { DBGBUS_SSPP0, 22, 4 }, + { DBGBUS_SSPP0, 22, 5 }, + { DBGBUS_SSPP0, 22, 6 }, + { DBGBUS_SSPP0, 22, 7 }, + + { DBGBUS_SSPP0, 24, 0 }, + { DBGBUS_SSPP0, 24, 1 }, + { DBGBUS_SSPP0, 24, 2 }, + { DBGBUS_SSPP0, 24, 3 }, + { DBGBUS_SSPP0, 24, 4 }, + { DBGBUS_SSPP0, 24, 5 }, + { DBGBUS_SSPP0, 24, 6 }, + { DBGBUS_SSPP0, 24, 7 }, + + { DBGBUS_SSPP0, 25, 0 }, + { DBGBUS_SSPP0, 25, 1 }, + { DBGBUS_SSPP0, 25, 2 }, + { DBGBUS_SSPP0, 25, 3 }, + { DBGBUS_SSPP0, 25, 4 }, + { DBGBUS_SSPP0, 25, 5 }, + { DBGBUS_SSPP0, 25, 6 }, + { DBGBUS_SSPP0, 25, 7 }, + + /* dma 2 */ + { DBGBUS_SSPP0, 30, 0 }, + { DBGBUS_SSPP0, 30, 1 }, + { DBGBUS_SSPP0, 30, 2 }, + { DBGBUS_SSPP0, 30, 3 }, + { DBGBUS_SSPP0, 30, 4 }, + { DBGBUS_SSPP0, 30, 5 }, + { DBGBUS_SSPP0, 30, 6 }, + { DBGBUS_SSPP0, 30, 7 }, + + { DBGBUS_SSPP0, 31, 0 }, + { DBGBUS_SSPP0, 31, 1 }, + { DBGBUS_SSPP0, 31, 2 }, + { DBGBUS_SSPP0, 31, 3 }, + { DBGBUS_SSPP0, 31, 4 }, + { DBGBUS_SSPP0, 31, 5 }, + { DBGBUS_SSPP0, 31, 6 }, + { DBGBUS_SSPP0, 31, 7 }, + + { DBGBUS_SSPP0, 32, 0 }, + { DBGBUS_SSPP0, 32, 1 }, + { DBGBUS_SSPP0, 32, 2 }, + { DBGBUS_SSPP0, 32, 3 }, + { DBGBUS_SSPP0, 32, 4 }, + { DBGBUS_SSPP0, 32, 5 }, + { DBGBUS_SSPP0, 32, 6 }, + { DBGBUS_SSPP0, 32, 7 }, + + { DBGBUS_SSPP0, 33, 0 }, + { DBGBUS_SSPP0, 33, 1 }, + { DBGBUS_SSPP0, 33, 2 }, + { DBGBUS_SSPP0, 33, 3 }, + { DBGBUS_SSPP0, 33, 4 }, + { DBGBUS_SSPP0, 33, 5 }, + { DBGBUS_SSPP0, 33, 6 }, + { DBGBUS_SSPP0, 33, 7 }, + + { DBGBUS_SSPP0, 34, 0 }, + { DBGBUS_SSPP0, 34, 1 }, + { DBGBUS_SSPP0, 34, 2 }, + { DBGBUS_SSPP0, 34, 3 }, + { DBGBUS_SSPP0, 34, 4 }, + { DBGBUS_SSPP0, 34, 5 }, + { DBGBUS_SSPP0, 34, 6 }, + { DBGBUS_SSPP0, 34, 7 }, + + { DBGBUS_SSPP0, 35, 0 }, + { DBGBUS_SSPP0, 35, 1 }, + { DBGBUS_SSPP0, 35, 2 }, + { DBGBUS_SSPP0, 35, 3 }, + + /* dma 0 */ + { DBGBUS_SSPP0, 40, 0 }, + { DBGBUS_SSPP0, 40, 1 }, + { DBGBUS_SSPP0, 40, 2 }, + { DBGBUS_SSPP0, 40, 3 }, + { DBGBUS_SSPP0, 40, 4 }, + { DBGBUS_SSPP0, 40, 5 }, + { DBGBUS_SSPP0, 40, 6 }, + { DBGBUS_SSPP0, 40, 7 }, + + { DBGBUS_SSPP0, 41, 0 }, + { DBGBUS_SSPP0, 41, 1 }, + { DBGBUS_SSPP0, 41, 2 }, + { DBGBUS_SSPP0, 41, 3 }, + { DBGBUS_SSPP0, 41, 4 }, + { DBGBUS_SSPP0, 41, 5 }, + { DBGBUS_SSPP0, 41, 6 }, + { DBGBUS_SSPP0, 41, 7 }, + + { DBGBUS_SSPP0, 42, 0 }, + { DBGBUS_SSPP0, 42, 1 }, + { DBGBUS_SSPP0, 42, 2 }, + { DBGBUS_SSPP0, 42, 3 }, + { DBGBUS_SSPP0, 42, 4 }, + { DBGBUS_SSPP0, 42, 5 }, + { DBGBUS_SSPP0, 42, 6 }, + { DBGBUS_SSPP0, 42, 7 }, + + { DBGBUS_SSPP0, 44, 0 }, + { DBGBUS_SSPP0, 44, 1 }, + { DBGBUS_SSPP0, 44, 2 }, + { DBGBUS_SSPP0, 44, 3 }, + { DBGBUS_SSPP0, 44, 4 }, + { DBGBUS_SSPP0, 44, 5 }, + { DBGBUS_SSPP0, 44, 6 }, + { DBGBUS_SSPP0, 44, 7 }, + + { DBGBUS_SSPP0, 45, 0 }, + { DBGBUS_SSPP0, 45, 1 }, + { DBGBUS_SSPP0, 45, 2 }, + { DBGBUS_SSPP0, 45, 3 }, + { DBGBUS_SSPP0, 45, 4 }, + { DBGBUS_SSPP0, 45, 5 }, + { DBGBUS_SSPP0, 45, 6 }, + { DBGBUS_SSPP0, 45, 7 }, + + /* fetch sspp1 */ + /* vig 1 */ + { DBGBUS_SSPP1, 0, 0 }, + { DBGBUS_SSPP1, 0, 1 }, + { DBGBUS_SSPP1, 0, 2 }, + { DBGBUS_SSPP1, 0, 3 }, + { DBGBUS_SSPP1, 0, 4 }, + { DBGBUS_SSPP1, 0, 5 }, + { DBGBUS_SSPP1, 0, 6 }, + { DBGBUS_SSPP1, 0, 7 }, + + { DBGBUS_SSPP1, 1, 0 }, + { DBGBUS_SSPP1, 1, 1 }, + { DBGBUS_SSPP1, 1, 2 }, + { DBGBUS_SSPP1, 1, 3 }, + { DBGBUS_SSPP1, 1, 4 }, + { DBGBUS_SSPP1, 1, 5 }, + { DBGBUS_SSPP1, 1, 6 }, + { DBGBUS_SSPP1, 1, 7 }, + + { DBGBUS_SSPP1, 2, 0 }, + { DBGBUS_SSPP1, 2, 1 }, + { DBGBUS_SSPP1, 2, 2 }, + { DBGBUS_SSPP1, 2, 3 }, + { DBGBUS_SSPP1, 2, 4 }, + { DBGBUS_SSPP1, 2, 5 }, + { DBGBUS_SSPP1, 2, 6 }, + { DBGBUS_SSPP1, 2, 7 }, + + { DBGBUS_SSPP1, 4, 0 }, + { DBGBUS_SSPP1, 4, 1 }, + { DBGBUS_SSPP1, 4, 2 }, + { DBGBUS_SSPP1, 4, 3 }, + { DBGBUS_SSPP1, 4, 4 }, + { DBGBUS_SSPP1, 4, 5 }, + { DBGBUS_SSPP1, 4, 6 }, + { DBGBUS_SSPP1, 4, 7 }, + + { DBGBUS_SSPP1, 5, 0 }, + { DBGBUS_SSPP1, 5, 1 }, + { DBGBUS_SSPP1, 5, 2 }, + { DBGBUS_SSPP1, 5, 3 }, + { DBGBUS_SSPP1, 5, 4 }, + { DBGBUS_SSPP1, 5, 5 }, + { DBGBUS_SSPP1, 5, 6 }, + { DBGBUS_SSPP1, 5, 7 }, + + /* vig 3 */ + { DBGBUS_SSPP1, 20, 0 }, + { DBGBUS_SSPP1, 20, 1 }, + { DBGBUS_SSPP1, 20, 2 }, + { DBGBUS_SSPP1, 20, 3 }, + { DBGBUS_SSPP1, 20, 4 }, + { DBGBUS_SSPP1, 20, 5 }, + { DBGBUS_SSPP1, 20, 6 }, + { DBGBUS_SSPP1, 20, 7 }, + + { DBGBUS_SSPP1, 21, 0 }, + { DBGBUS_SSPP1, 21, 1 }, + { DBGBUS_SSPP1, 21, 2 }, + { DBGBUS_SSPP1, 21, 3 }, + { DBGBUS_SSPP1, 21, 4 }, + { DBGBUS_SSPP1, 21, 5 }, + { DBGBUS_SSPP1, 21, 6 }, + { DBGBUS_SSPP1, 21, 7 }, + + { DBGBUS_SSPP1, 22, 0 }, + { DBGBUS_SSPP1, 22, 1 }, + { DBGBUS_SSPP1, 22, 2 }, + { DBGBUS_SSPP1, 22, 3 }, + { DBGBUS_SSPP1, 22, 4 }, + { DBGBUS_SSPP1, 22, 5 }, + { DBGBUS_SSPP1, 22, 6 }, + { DBGBUS_SSPP1, 22, 7 }, + + { DBGBUS_SSPP1, 24, 0 }, + { DBGBUS_SSPP1, 24, 1 }, + { DBGBUS_SSPP1, 24, 2 }, + { DBGBUS_SSPP1, 24, 3 }, + { DBGBUS_SSPP1, 24, 4 }, + { DBGBUS_SSPP1, 24, 5 }, + { DBGBUS_SSPP1, 24, 6 }, + { DBGBUS_SSPP1, 24, 7 }, + + { DBGBUS_SSPP1, 25, 0 }, + { DBGBUS_SSPP1, 25, 1 }, + { DBGBUS_SSPP1, 25, 2 }, + { DBGBUS_SSPP1, 25, 3 }, + { DBGBUS_SSPP1, 25, 4 }, + { DBGBUS_SSPP1, 25, 5 }, + { DBGBUS_SSPP1, 25, 6 }, + { DBGBUS_SSPP1, 25, 7 }, + + /* dma 3 */ + { DBGBUS_SSPP1, 30, 0 }, + { DBGBUS_SSPP1, 30, 1 }, + { DBGBUS_SSPP1, 30, 2 }, + { DBGBUS_SSPP1, 30, 3 }, + { DBGBUS_SSPP1, 30, 4 }, + { DBGBUS_SSPP1, 30, 5 }, + { DBGBUS_SSPP1, 30, 6 }, + { DBGBUS_SSPP1, 30, 7 }, + + { DBGBUS_SSPP1, 31, 0 }, + { DBGBUS_SSPP1, 31, 1 }, + { DBGBUS_SSPP1, 31, 2 }, + { DBGBUS_SSPP1, 31, 3 }, + { DBGBUS_SSPP1, 31, 4 }, + { DBGBUS_SSPP1, 31, 5 }, + { DBGBUS_SSPP1, 31, 6 }, + { DBGBUS_SSPP1, 31, 7 }, + + { DBGBUS_SSPP1, 32, 0 }, + { DBGBUS_SSPP1, 32, 1 }, + { DBGBUS_SSPP1, 32, 2 }, + { DBGBUS_SSPP1, 32, 3 }, + { DBGBUS_SSPP1, 32, 4 }, + { DBGBUS_SSPP1, 32, 5 }, + { DBGBUS_SSPP1, 32, 6 }, + { DBGBUS_SSPP1, 32, 7 }, + + { DBGBUS_SSPP1, 33, 0 }, + { DBGBUS_SSPP1, 33, 1 }, + { DBGBUS_SSPP1, 33, 2 }, + { DBGBUS_SSPP1, 33, 3 }, + { DBGBUS_SSPP1, 33, 4 }, + { DBGBUS_SSPP1, 33, 5 }, + { DBGBUS_SSPP1, 33, 6 }, + { DBGBUS_SSPP1, 33, 7 }, + + { DBGBUS_SSPP1, 34, 0 }, + { DBGBUS_SSPP1, 34, 1 }, + { DBGBUS_SSPP1, 34, 2 }, + { DBGBUS_SSPP1, 34, 3 }, + { DBGBUS_SSPP1, 34, 4 }, + { DBGBUS_SSPP1, 34, 5 }, + { DBGBUS_SSPP1, 34, 6 }, + { DBGBUS_SSPP1, 34, 7 }, + + { DBGBUS_SSPP1, 35, 0 }, + { DBGBUS_SSPP1, 35, 1 }, + { DBGBUS_SSPP1, 35, 2 }, + + /* dma 1 */ + { DBGBUS_SSPP1, 40, 0 }, + { DBGBUS_SSPP1, 40, 1 }, + { DBGBUS_SSPP1, 40, 2 }, + { DBGBUS_SSPP1, 40, 3 }, + { DBGBUS_SSPP1, 40, 4 }, + { DBGBUS_SSPP1, 40, 5 }, + { DBGBUS_SSPP1, 40, 6 }, + { DBGBUS_SSPP1, 40, 7 }, + + { DBGBUS_SSPP1, 41, 0 }, + { DBGBUS_SSPP1, 41, 1 }, + { DBGBUS_SSPP1, 41, 2 }, + { DBGBUS_SSPP1, 41, 3 }, + { DBGBUS_SSPP1, 41, 4 }, + { DBGBUS_SSPP1, 41, 5 }, + { DBGBUS_SSPP1, 41, 6 }, + { DBGBUS_SSPP1, 41, 7 }, + + { DBGBUS_SSPP1, 42, 0 }, + { DBGBUS_SSPP1, 42, 1 }, + { DBGBUS_SSPP1, 42, 2 }, + { DBGBUS_SSPP1, 42, 3 }, + { DBGBUS_SSPP1, 42, 4 }, + { DBGBUS_SSPP1, 42, 5 }, + { DBGBUS_SSPP1, 42, 6 }, + { DBGBUS_SSPP1, 42, 7 }, + + { DBGBUS_SSPP1, 44, 0 }, + { DBGBUS_SSPP1, 44, 1 }, + { DBGBUS_SSPP1, 44, 2 }, + { DBGBUS_SSPP1, 44, 3 }, + { DBGBUS_SSPP1, 44, 4 }, + { DBGBUS_SSPP1, 44, 5 }, + { DBGBUS_SSPP1, 44, 6 }, + { DBGBUS_SSPP1, 44, 7 }, + + { DBGBUS_SSPP1, 45, 0 }, + { DBGBUS_SSPP1, 45, 1 }, + { DBGBUS_SSPP1, 45, 2 }, + { DBGBUS_SSPP1, 45, 3 }, + { DBGBUS_SSPP1, 45, 4 }, + { DBGBUS_SSPP1, 45, 5 }, + { DBGBUS_SSPP1, 45, 6 }, + { DBGBUS_SSPP1, 45, 7 }, + + /* cursor 1 */ + { DBGBUS_SSPP1, 80, 0 }, + { DBGBUS_SSPP1, 80, 1 }, + { DBGBUS_SSPP1, 80, 2 }, + { DBGBUS_SSPP1, 80, 3 }, + { DBGBUS_SSPP1, 80, 4 }, + { DBGBUS_SSPP1, 80, 5 }, + { DBGBUS_SSPP1, 80, 6 }, + { DBGBUS_SSPP1, 80, 7 }, + + { DBGBUS_SSPP1, 81, 0 }, + { DBGBUS_SSPP1, 81, 1 }, + { DBGBUS_SSPP1, 81, 2 }, + { DBGBUS_SSPP1, 81, 3 }, + { DBGBUS_SSPP1, 81, 4 }, + { DBGBUS_SSPP1, 81, 5 }, + { DBGBUS_SSPP1, 81, 6 }, + { DBGBUS_SSPP1, 81, 7 }, + + { DBGBUS_SSPP1, 82, 0 }, + { DBGBUS_SSPP1, 82, 1 }, + { DBGBUS_SSPP1, 82, 2 }, + { DBGBUS_SSPP1, 82, 3 }, + { DBGBUS_SSPP1, 82, 4 }, + { DBGBUS_SSPP1, 82, 5 }, + { DBGBUS_SSPP1, 82, 6 }, + { DBGBUS_SSPP1, 82, 7 }, + + { DBGBUS_SSPP1, 83, 0 }, + { DBGBUS_SSPP1, 83, 1 }, + { DBGBUS_SSPP1, 83, 2 }, + { DBGBUS_SSPP1, 83, 3 }, + { DBGBUS_SSPP1, 83, 4 }, + { DBGBUS_SSPP1, 83, 5 }, + { DBGBUS_SSPP1, 83, 6 }, + { DBGBUS_SSPP1, 83, 7 }, + + { DBGBUS_SSPP1, 84, 0 }, + { DBGBUS_SSPP1, 84, 1 }, + { DBGBUS_SSPP1, 84, 2 }, + { DBGBUS_SSPP1, 84, 3 }, + { DBGBUS_SSPP1, 84, 4 }, + { DBGBUS_SSPP1, 84, 5 }, + { DBGBUS_SSPP1, 84, 6 }, + { DBGBUS_SSPP1, 84, 7 }, + + /* dspp */ + { DBGBUS_DSPP, 13, 0 }, + { DBGBUS_DSPP, 19, 0 }, + { DBGBUS_DSPP, 14, 0 }, + { DBGBUS_DSPP, 14, 1 }, + { DBGBUS_DSPP, 14, 3 }, + { DBGBUS_DSPP, 20, 0 }, + { DBGBUS_DSPP, 20, 1 }, + { DBGBUS_DSPP, 20, 3 }, + + /* ppb_0 */ + { DBGBUS_DSPP, 31, 0 }, + { DBGBUS_DSPP, 33, 0 }, + { DBGBUS_DSPP, 35, 0 }, + { DBGBUS_DSPP, 42, 0 }, + + /* ppb_1 */ + { DBGBUS_DSPP, 32, 0 }, + { DBGBUS_DSPP, 34, 0 }, + { DBGBUS_DSPP, 36, 0 }, + { DBGBUS_DSPP, 43, 0 }, + + /* lm_lut */ + { DBGBUS_DSPP, 109, 0 }, + { DBGBUS_DSPP, 105, 0 }, + { DBGBUS_DSPP, 103, 0 }, + + /* tear-check */ + { DBGBUS_PERIPH, 63, 0 }, + { DBGBUS_PERIPH, 64, 0 }, + { DBGBUS_PERIPH, 65, 0 }, + { DBGBUS_PERIPH, 73, 0 }, + { DBGBUS_PERIPH, 74, 0 }, + + /* crossbar */ + { DBGBUS_DSPP, 0, 0}, + + /* rotator */ + { DBGBUS_DSPP, 9, 0}, + + /* blend */ + /* LM0 */ + { DBGBUS_DSPP, 63, 0}, + { DBGBUS_DSPP, 63, 1}, + { DBGBUS_DSPP, 63, 2}, + { DBGBUS_DSPP, 63, 3}, + { DBGBUS_DSPP, 63, 4}, + { DBGBUS_DSPP, 63, 5}, + { DBGBUS_DSPP, 63, 6}, + { DBGBUS_DSPP, 63, 7}, + + { DBGBUS_DSPP, 64, 0}, + { DBGBUS_DSPP, 64, 1}, + { DBGBUS_DSPP, 64, 2}, + { DBGBUS_DSPP, 64, 3}, + { DBGBUS_DSPP, 64, 4}, + { DBGBUS_DSPP, 64, 5}, + { DBGBUS_DSPP, 64, 6}, + { DBGBUS_DSPP, 64, 7}, + + { DBGBUS_DSPP, 65, 0}, + { DBGBUS_DSPP, 65, 1}, + { DBGBUS_DSPP, 65, 2}, + { DBGBUS_DSPP, 65, 3}, + { DBGBUS_DSPP, 65, 4}, + { DBGBUS_DSPP, 65, 5}, + { DBGBUS_DSPP, 65, 6}, + { DBGBUS_DSPP, 65, 7}, + + { DBGBUS_DSPP, 66, 0}, + { DBGBUS_DSPP, 66, 1}, + { DBGBUS_DSPP, 66, 2}, + { DBGBUS_DSPP, 66, 3}, + { DBGBUS_DSPP, 66, 4}, + { DBGBUS_DSPP, 66, 5}, + { DBGBUS_DSPP, 66, 6}, + { DBGBUS_DSPP, 66, 7}, + + { DBGBUS_DSPP, 67, 0}, + { DBGBUS_DSPP, 67, 1}, + { DBGBUS_DSPP, 67, 2}, + { DBGBUS_DSPP, 67, 3}, + { DBGBUS_DSPP, 67, 4}, + { DBGBUS_DSPP, 67, 5}, + { DBGBUS_DSPP, 67, 6}, + { DBGBUS_DSPP, 67, 7}, + + { DBGBUS_DSPP, 68, 0}, + { DBGBUS_DSPP, 68, 1}, + { DBGBUS_DSPP, 68, 2}, + { DBGBUS_DSPP, 68, 3}, + { DBGBUS_DSPP, 68, 4}, + { DBGBUS_DSPP, 68, 5}, + { DBGBUS_DSPP, 68, 6}, + { DBGBUS_DSPP, 68, 7}, + + { DBGBUS_DSPP, 69, 0}, + { DBGBUS_DSPP, 69, 1}, + { DBGBUS_DSPP, 69, 2}, + { DBGBUS_DSPP, 69, 3}, + { DBGBUS_DSPP, 69, 4}, + { DBGBUS_DSPP, 69, 5}, + { DBGBUS_DSPP, 69, 6}, + { DBGBUS_DSPP, 69, 7}, + + /* LM1 */ + { DBGBUS_DSPP, 70, 0}, + { DBGBUS_DSPP, 70, 1}, + { DBGBUS_DSPP, 70, 2}, + { DBGBUS_DSPP, 70, 3}, + { DBGBUS_DSPP, 70, 4}, + { DBGBUS_DSPP, 70, 5}, + { DBGBUS_DSPP, 70, 6}, + { DBGBUS_DSPP, 70, 7}, + + { DBGBUS_DSPP, 71, 0}, + { DBGBUS_DSPP, 71, 1}, + { DBGBUS_DSPP, 71, 2}, + { DBGBUS_DSPP, 71, 3}, + { DBGBUS_DSPP, 71, 4}, + { DBGBUS_DSPP, 71, 5}, + { DBGBUS_DSPP, 71, 6}, + { DBGBUS_DSPP, 71, 7}, + + { DBGBUS_DSPP, 72, 0}, + { DBGBUS_DSPP, 72, 1}, + { DBGBUS_DSPP, 72, 2}, + { DBGBUS_DSPP, 72, 3}, + { DBGBUS_DSPP, 72, 4}, + { DBGBUS_DSPP, 72, 5}, + { DBGBUS_DSPP, 72, 6}, + { DBGBUS_DSPP, 72, 7}, + + { DBGBUS_DSPP, 73, 0}, + { DBGBUS_DSPP, 73, 1}, + { DBGBUS_DSPP, 73, 2}, + { DBGBUS_DSPP, 73, 3}, + { DBGBUS_DSPP, 73, 4}, + { DBGBUS_DSPP, 73, 5}, + { DBGBUS_DSPP, 73, 6}, + { DBGBUS_DSPP, 73, 7}, + + { DBGBUS_DSPP, 74, 0}, + { DBGBUS_DSPP, 74, 1}, + { DBGBUS_DSPP, 74, 2}, + { DBGBUS_DSPP, 74, 3}, + { DBGBUS_DSPP, 74, 4}, + { DBGBUS_DSPP, 74, 5}, + { DBGBUS_DSPP, 74, 6}, + { DBGBUS_DSPP, 74, 7}, + + { DBGBUS_DSPP, 75, 0}, + { DBGBUS_DSPP, 75, 1}, + { DBGBUS_DSPP, 75, 2}, + { DBGBUS_DSPP, 75, 3}, + { DBGBUS_DSPP, 75, 4}, + { DBGBUS_DSPP, 75, 5}, + { DBGBUS_DSPP, 75, 6}, + { DBGBUS_DSPP, 75, 7}, + + { DBGBUS_DSPP, 76, 0}, + { DBGBUS_DSPP, 76, 1}, + { DBGBUS_DSPP, 76, 2}, + { DBGBUS_DSPP, 76, 3}, + { DBGBUS_DSPP, 76, 4}, + { DBGBUS_DSPP, 76, 5}, + { DBGBUS_DSPP, 76, 6}, + { DBGBUS_DSPP, 76, 7}, + + /* LM2 */ + { DBGBUS_DSPP, 77, 0}, + { DBGBUS_DSPP, 77, 1}, + { DBGBUS_DSPP, 77, 2}, + { DBGBUS_DSPP, 77, 3}, + { DBGBUS_DSPP, 77, 4}, + { DBGBUS_DSPP, 77, 5}, + { DBGBUS_DSPP, 77, 6}, + { DBGBUS_DSPP, 77, 7}, + + { DBGBUS_DSPP, 78, 0}, + { DBGBUS_DSPP, 78, 1}, + { DBGBUS_DSPP, 78, 2}, + { DBGBUS_DSPP, 78, 3}, + { DBGBUS_DSPP, 78, 4}, + { DBGBUS_DSPP, 78, 5}, + { DBGBUS_DSPP, 78, 6}, + { DBGBUS_DSPP, 78, 7}, + + { DBGBUS_DSPP, 79, 0}, + { DBGBUS_DSPP, 79, 1}, + { DBGBUS_DSPP, 79, 2}, + { DBGBUS_DSPP, 79, 3}, + { DBGBUS_DSPP, 79, 4}, + { DBGBUS_DSPP, 79, 5}, + { DBGBUS_DSPP, 79, 6}, + { DBGBUS_DSPP, 79, 7}, + + { DBGBUS_DSPP, 80, 0}, + { DBGBUS_DSPP, 80, 1}, + { DBGBUS_DSPP, 80, 2}, + { DBGBUS_DSPP, 80, 3}, + { DBGBUS_DSPP, 80, 4}, + { DBGBUS_DSPP, 80, 5}, + { DBGBUS_DSPP, 80, 6}, + { DBGBUS_DSPP, 80, 7}, + + { DBGBUS_DSPP, 81, 0}, + { DBGBUS_DSPP, 81, 1}, + { DBGBUS_DSPP, 81, 2}, + { DBGBUS_DSPP, 81, 3}, + { DBGBUS_DSPP, 81, 4}, + { DBGBUS_DSPP, 81, 5}, + { DBGBUS_DSPP, 81, 6}, + { DBGBUS_DSPP, 81, 7}, + + { DBGBUS_DSPP, 82, 0}, + { DBGBUS_DSPP, 82, 1}, + { DBGBUS_DSPP, 82, 2}, + { DBGBUS_DSPP, 82, 3}, + { DBGBUS_DSPP, 82, 4}, + { DBGBUS_DSPP, 82, 5}, + { DBGBUS_DSPP, 82, 6}, + { DBGBUS_DSPP, 82, 7}, + + { DBGBUS_DSPP, 83, 0}, + { DBGBUS_DSPP, 83, 1}, + { DBGBUS_DSPP, 83, 2}, + { DBGBUS_DSPP, 83, 3}, + { DBGBUS_DSPP, 83, 4}, + { DBGBUS_DSPP, 83, 5}, + { DBGBUS_DSPP, 83, 6}, + { DBGBUS_DSPP, 83, 7}, + + /* csc */ + { DBGBUS_SSPP0, 7, 0}, + { DBGBUS_SSPP0, 7, 1}, + { DBGBUS_SSPP0, 27, 0}, + { DBGBUS_SSPP0, 27, 1}, + { DBGBUS_SSPP1, 7, 0}, + { DBGBUS_SSPP1, 7, 1}, + { DBGBUS_SSPP1, 27, 0}, + { DBGBUS_SSPP1, 27, 1}, + + /* pcc */ + { DBGBUS_SSPP0, 3, 3}, + { DBGBUS_SSPP0, 23, 3}, + { DBGBUS_SSPP0, 33, 3}, + { DBGBUS_SSPP0, 43, 3}, + { DBGBUS_SSPP1, 3, 3}, + { DBGBUS_SSPP1, 23, 3}, + { DBGBUS_SSPP1, 33, 3}, + { DBGBUS_SSPP1, 43, 3}, + + /* spa */ + { DBGBUS_SSPP0, 8, 0}, + { DBGBUS_SSPP0, 28, 0}, + { DBGBUS_SSPP1, 8, 0}, + { DBGBUS_SSPP1, 28, 0}, + { DBGBUS_DSPP, 13, 0}, + { DBGBUS_DSPP, 19, 0}, + + /* igc */ + { DBGBUS_SSPP0, 9, 0}, + { DBGBUS_SSPP0, 9, 1}, + { DBGBUS_SSPP0, 9, 3}, + { DBGBUS_SSPP0, 29, 0}, + { DBGBUS_SSPP0, 29, 1}, + { DBGBUS_SSPP0, 29, 3}, + { DBGBUS_SSPP0, 17, 0}, + { DBGBUS_SSPP0, 17, 1}, + { DBGBUS_SSPP0, 17, 3}, + { DBGBUS_SSPP0, 37, 0}, + { DBGBUS_SSPP0, 37, 1}, + { DBGBUS_SSPP0, 37, 3}, + { DBGBUS_SSPP0, 46, 0}, + { DBGBUS_SSPP0, 46, 1}, + { DBGBUS_SSPP0, 46, 3}, + + { DBGBUS_SSPP1, 9, 0}, + { DBGBUS_SSPP1, 9, 1}, + { DBGBUS_SSPP1, 9, 3}, + { DBGBUS_SSPP1, 29, 0}, + { DBGBUS_SSPP1, 29, 1}, + { DBGBUS_SSPP1, 29, 3}, + { DBGBUS_SSPP1, 17, 0}, + { DBGBUS_SSPP1, 17, 1}, + { DBGBUS_SSPP1, 17, 3}, + { DBGBUS_SSPP1, 37, 0}, + { DBGBUS_SSPP1, 37, 1}, + { DBGBUS_SSPP1, 37, 3}, + { DBGBUS_SSPP1, 46, 0}, + { DBGBUS_SSPP1, 46, 1}, + { DBGBUS_SSPP1, 46, 3}, + + { DBGBUS_DSPP, 14, 0}, + { DBGBUS_DSPP, 14, 1}, + { DBGBUS_DSPP, 14, 3}, + { DBGBUS_DSPP, 20, 0}, + { DBGBUS_DSPP, 20, 1}, + { DBGBUS_DSPP, 20, 3}, + + { DBGBUS_PERIPH, 60, 0}, +}; + +static struct vbif_debug_bus_entry vbif_dbg_bus_msm8998[] = { + {0x214, 0x21c, 16, 2, 0x0, 0xd}, /* arb clients */ + {0x214, 0x21c, 16, 2, 0x80, 0xc0}, /* arb clients */ + {0x214, 0x21c, 16, 2, 0x100, 0x140}, /* arb clients */ + {0x214, 0x21c, 0, 16, 0x0, 0xf}, /* xin blocks - axi side */ + {0x214, 0x21c, 0, 16, 0x80, 0xa4}, /* xin blocks - axi side */ + {0x214, 0x21c, 0, 15, 0x100, 0x124}, /* xin blocks - axi side */ + {0x21c, 0x214, 0, 14, 0, 0xc}, /* xin blocks - clock side */ +}; + +/** + * _sde_dbg_enable_power - use callback to turn power on for hw register access + * @enable: whether to turn power on or off + */ +static inline void _sde_dbg_enable_power(int enable) +{ + if (!sde_dbg_base.power_ctrl.enable_fn) + return; + sde_dbg_base.power_ctrl.enable_fn( + sde_dbg_base.power_ctrl.handle, + sde_dbg_base.power_ctrl.client, + enable); +} + +/** + * _sde_dump_reg - helper function for dumping rotator register set content + * @dump_name: register set name + * @reg_dump_flag: dumping flag controlling in-log/memory dump location + * @base_addr: starting address of io region for calculating offsets to print + * @addr: starting address offset for dumping + * @len_bytes: range of the register set + * @dump_mem: output buffer for memory dump location option + * @from_isr: whether being called from isr context + */ +static void _sde_dump_reg(const char *dump_name, u32 reg_dump_flag, + char __iomem *base_addr, char __iomem *addr, size_t len_bytes, + u32 **dump_mem, bool from_isr) +{ + u32 in_log, in_mem, len_align, len_padded; + u32 *dump_addr = NULL; + char __iomem *end_addr; + int i; + + if (!len_bytes) + return; + + in_log = (reg_dump_flag & SDE_DBG_DUMP_IN_LOG); + in_mem = (reg_dump_flag & SDE_DBG_DUMP_IN_MEM); + + pr_debug("%s: reg_dump_flag=%d in_log=%d in_mem=%d\n", + dump_name, reg_dump_flag, in_log, in_mem); + + if (!in_log && !in_mem) + return; + + if (in_log) + dev_info(sde_dbg_base.dev, "%s: start_offset 0x%lx len 0x%zx\n", + dump_name, addr - base_addr, len_bytes); + + len_align = (len_bytes + REG_DUMP_ALIGN - 1) / REG_DUMP_ALIGN; + len_padded = len_align * REG_DUMP_ALIGN; + end_addr = addr + len_bytes; + + if (in_mem) { + if (dump_mem && !(*dump_mem)) { + phys_addr_t phys = 0; + *dump_mem = dma_alloc_coherent(sde_dbg_base.dev, + len_padded, &phys, GFP_KERNEL); + } + + if (dump_mem && *dump_mem) { + dump_addr = *dump_mem; + dev_info(sde_dbg_base.dev, + "%s: start_addr:0x%pK len:0x%x reg_offset=0x%lx\n", + dump_name, dump_addr, len_padded, + addr - base_addr); + } else { + in_mem = 0; + pr_err("dump_mem: kzalloc fails!\n"); + } + } + + if (!from_isr) + _sde_dbg_enable_power(true); + + for (i = 0; i < len_align; i++) { + u32 x0, x4, x8, xc; + + x0 = (addr < end_addr) ? readl_relaxed(addr + 0x0) : 0; + x4 = (addr + 0x4 < end_addr) ? readl_relaxed(addr + 0x4) : 0; + x8 = (addr + 0x8 < end_addr) ? readl_relaxed(addr + 0x8) : 0; + xc = (addr + 0xc < end_addr) ? readl_relaxed(addr + 0xc) : 0; + + if (in_log) + dev_info(sde_dbg_base.dev, + "0x%lx : %08x %08x %08x %08x\n", + addr - base_addr, x0, x4, x8, xc); + + if (dump_addr) { + dump_addr[i * 4] = x0; + dump_addr[i * 4 + 1] = x4; + dump_addr[i * 4 + 2] = x8; + dump_addr[i * 4 + 3] = xc; + } + + addr += REG_DUMP_ALIGN; + } + + if (!from_isr) + _sde_dbg_enable_power(false); +} + +/** + * _sde_dbg_get_dump_range - helper to retrieve dump length for a range node + * @range_node: range node to dump + * @max_offset: max offset of the register base + * @Return: length + */ +static u32 _sde_dbg_get_dump_range(struct sde_dbg_reg_offset *range_node, + size_t max_offset) +{ + u32 length = 0; + + if ((range_node->start > range_node->end) || + (range_node->end > max_offset) || (range_node->start == 0 + && range_node->end == 0)) { + length = max_offset; + } else { + length = range_node->end - range_node->start; + } + + return length; +} + +static int _sde_dump_reg_range_cmp(void *priv, struct list_head *a, + struct list_head *b) +{ + struct sde_dbg_reg_range *ar, *br; + + if (!a || !b) + return 0; + + ar = container_of(a, struct sde_dbg_reg_range, head); + br = container_of(b, struct sde_dbg_reg_range, head); + + return ar->offset.start - br->offset.start; +} + +/** + * _sde_dump_reg_by_ranges - dump ranges or full range of the register blk base + * @dbg: register blk base structure + * @reg_dump_flag: dump target, memory, kernel log, or both + */ +static void _sde_dump_reg_by_ranges(struct sde_dbg_reg_base *dbg, + u32 reg_dump_flag) +{ + char __iomem *addr; + size_t len; + struct sde_dbg_reg_range *range_node; + + if (!dbg || !dbg->base) { + pr_err("dbg base is null!\n"); + return; + } + + dev_info(sde_dbg_base.dev, "%s:=========%s DUMP=========\n", __func__, + dbg->name); + + /* If there is a list to dump the registers by ranges, use the ranges */ + if (!list_empty(&dbg->sub_range_list)) { + /* sort the list by start address first */ + list_sort(NULL, &dbg->sub_range_list, _sde_dump_reg_range_cmp); + list_for_each_entry(range_node, &dbg->sub_range_list, head) { + len = _sde_dbg_get_dump_range(&range_node->offset, + dbg->max_offset); + addr = dbg->base + range_node->offset.start; + pr_debug("%s: range_base=0x%pK start=0x%x end=0x%x\n", + range_node->range_name, + addr, range_node->offset.start, + range_node->offset.end); + + _sde_dump_reg(range_node->range_name, reg_dump_flag, + dbg->base, addr, len, + &range_node->reg_dump, false); + } + } else { + /* If there is no list to dump ranges, dump all registers */ + dev_info(sde_dbg_base.dev, + "Ranges not found, will dump full registers\n"); + dev_info(sde_dbg_base.dev, "base:0x%pK len:0x%zx\n", dbg->base, + dbg->max_offset); + addr = dbg->base; + len = dbg->max_offset; + _sde_dump_reg(dbg->name, reg_dump_flag, dbg->base, addr, len, + &dbg->reg_dump, false); + } +} + +/** + * _sde_dump_reg_by_blk - dump a named register base region + * @blk_name: register blk name + */ +static void _sde_dump_reg_by_blk(const char *blk_name) +{ + struct sde_dbg_base *dbg_base = &sde_dbg_base; + struct sde_dbg_reg_base *blk_base; + + if (!dbg_base) + return; + + list_for_each_entry(blk_base, &dbg_base->reg_base_list, reg_base_head) { + if (strlen(blk_base->name) && + !strcmp(blk_base->name, blk_name)) { + _sde_dump_reg_by_ranges(blk_base, + dbg_base->enable_reg_dump); + break; + } + } +} + +/** + * _sde_dump_reg_all - dump all register regions + */ +static void _sde_dump_reg_all(void) +{ + struct sde_dbg_base *dbg_base = &sde_dbg_base; + struct sde_dbg_reg_base *blk_base; + + if (!dbg_base) + return; + + list_for_each_entry(blk_base, &dbg_base->reg_base_list, reg_base_head) + if (strlen(blk_base->name)) + _sde_dump_reg_by_blk(blk_base->name); +} + +/** + * _sde_dump_get_blk_addr - retrieve register block address by name + * @blk_name: register blk name + * @Return: register blk base, or NULL + */ +static struct sde_dbg_reg_base *_sde_dump_get_blk_addr(const char *blk_name) +{ + struct sde_dbg_base *dbg_base = &sde_dbg_base; + struct sde_dbg_reg_base *blk_base; + + list_for_each_entry(blk_base, &dbg_base->reg_base_list, reg_base_head) + if (strlen(blk_base->name) && !strcmp(blk_base->name, blk_name)) + return blk_base; + + return NULL; +} + +static void _sde_dbg_dump_sde_dbg_bus(struct sde_dbg_sde_debug_bus *bus) +{ + bool in_log, in_mem; + u32 **dump_mem = NULL; + u32 *dump_addr = NULL; + u32 status = 0; + struct sde_debug_bus_entry *head; + phys_addr_t phys = 0; + int list_size; + int i; + u32 offset; + void __iomem *mem_base = NULL; + struct sde_dbg_reg_base *reg_base; + + if (!bus || !bus->cmn.entries_size) + return; + + list_for_each_entry(reg_base, &sde_dbg_base.reg_base_list, + reg_base_head) + if (strlen(reg_base->name) && + !strcmp(reg_base->name, bus->cmn.name)) + mem_base = reg_base->base + bus->top_blk_off; + + if (!mem_base) { + pr_err("unable to find mem_base for %s\n", bus->cmn.name); + return; + } + + dump_mem = &bus->cmn.dumped_content; + + /* will keep in memory 4 entries of 4 bytes each */ + list_size = (bus->cmn.entries_size * 4 * 4); + + in_log = (bus->cmn.enable_mask & SDE_DBG_DUMP_IN_LOG); + in_mem = (bus->cmn.enable_mask & SDE_DBG_DUMP_IN_MEM); + + if (!in_log && !in_mem) + return; + + dev_info(sde_dbg_base.dev, "======== start %s dump =========\n", + bus->cmn.name); + + if (in_mem) { + if (!(*dump_mem)) + *dump_mem = dma_alloc_coherent(sde_dbg_base.dev, + list_size, &phys, GFP_KERNEL); + + if (*dump_mem) { + dump_addr = *dump_mem; + dev_info(sde_dbg_base.dev, + "%s: start_addr:0x%pK len:0x%x\n", + __func__, dump_addr, list_size); + } else { + in_mem = false; + pr_err("dump_mem: allocation fails\n"); + } + } + + _sde_dbg_enable_power(true); + for (i = 0; i < bus->cmn.entries_size; i++) { + head = bus->entries + i; + writel_relaxed(TEST_MASK(head->block_id, head->test_id), + mem_base + head->wr_addr); + wmb(); /* make sure test bits were written */ + + if (bus->cmn.flags & DBGBUS_FLAGS_DSPP) + offset = DBGBUS_DSPP_STATUS; + else + offset = head->wr_addr + 0x4; + + status = readl_relaxed(mem_base + offset); + + if (in_log) + dev_info(sde_dbg_base.dev, + "waddr=0x%x blk=%d tst=%d val=0x%x\n", + head->wr_addr, head->block_id, + head->test_id, status); + + if (dump_addr && in_mem) { + dump_addr[i*4] = head->wr_addr; + dump_addr[i*4 + 1] = head->block_id; + dump_addr[i*4 + 2] = head->test_id; + dump_addr[i*4 + 3] = status; + } + + /* Disable debug bus once we are done */ + writel_relaxed(0, mem_base + head->wr_addr); + + } + _sde_dbg_enable_power(false); + + dev_info(sde_dbg_base.dev, "======== end %s dump =========\n", + bus->cmn.name); +} + +static void _sde_dbg_dump_vbif_debug_bus_entry( + struct vbif_debug_bus_entry *head, void __iomem *mem_base, + u32 *dump_addr, bool in_log) +{ + int i, j; + u32 val; + + if (!dump_addr && !in_log) + return; + + for (i = 0; i < head->block_cnt; i++) { + writel_relaxed(1 << (i + head->bit_offset), + mem_base + head->block_bus_addr); + /* make sure that current bus blcok enable */ + wmb(); + for (j = head->test_pnt_start; j < head->test_pnt_cnt; j++) { + writel_relaxed(j, mem_base + head->block_bus_addr + 4); + /* make sure that test point is enabled */ + wmb(); + val = readl_relaxed(mem_base + MMSS_VBIF_TEST_BUS_OUT); + if (dump_addr) { + *dump_addr++ = head->block_bus_addr; + *dump_addr++ = i; + *dump_addr++ = j; + *dump_addr++ = val; + } + if (in_log) + dev_info(sde_dbg_base.dev, + "testpoint:%x arb/xin id=%d index=%d val=0x%x\n", + head->block_bus_addr, i, j, val); + } + } +} + +static void _sde_dbg_dump_vbif_dbg_bus(struct sde_dbg_vbif_debug_bus *bus) +{ + bool in_log, in_mem; + u32 **dump_mem = NULL; + u32 *dump_addr = NULL; + u32 value; + struct vbif_debug_bus_entry *head; + phys_addr_t phys = 0; + int i, list_size = 0; + void __iomem *mem_base = NULL; + struct vbif_debug_bus_entry *dbg_bus; + u32 bus_size; + struct sde_dbg_reg_base *reg_base; + + if (!bus || !bus->cmn.entries_size) + return; + + list_for_each_entry(reg_base, &sde_dbg_base.reg_base_list, + reg_base_head) + if (strlen(reg_base->name) && + !strcmp(reg_base->name, bus->cmn.name)) + mem_base = reg_base->base; + + if (!mem_base) { + pr_err("unable to find mem_base for %s\n", bus->cmn.name); + return; + } + + dbg_bus = bus->entries; + bus_size = bus->cmn.entries_size; + list_size = bus->cmn.entries_size; + dump_mem = &bus->cmn.dumped_content; + + dev_info(sde_dbg_base.dev, "======== start %s dump =========\n", + bus->cmn.name); + + if (!dump_mem || !dbg_bus || !bus_size || !list_size) + return; + + /* allocate memory for each test point */ + for (i = 0; i < bus_size; i++) { + head = dbg_bus + i; + list_size += (head->block_cnt * head->test_pnt_cnt); + } + + /* 4 bytes * 4 entries for each test point*/ + list_size *= 16; + + in_log = (bus->cmn.enable_mask & SDE_DBG_DUMP_IN_LOG); + in_mem = (bus->cmn.enable_mask & SDE_DBG_DUMP_IN_MEM); + + if (!in_log && !in_mem) + return; + + if (in_mem) { + if (!(*dump_mem)) + *dump_mem = dma_alloc_coherent(sde_dbg_base.dev, + list_size, &phys, GFP_KERNEL); + + if (*dump_mem) { + dump_addr = *dump_mem; + dev_info(sde_dbg_base.dev, + "%s: start_addr:0x%pK len:0x%x\n", + __func__, dump_addr, list_size); + } else { + in_mem = false; + pr_err("dump_mem: allocation fails\n"); + } + } + + _sde_dbg_enable_power(true); + + value = readl_relaxed(mem_base + MMSS_VBIF_CLKON); + writel_relaxed(value | BIT(1), mem_base + MMSS_VBIF_CLKON); + + /* make sure that vbif core is on */ + wmb(); + + for (i = 0; i < bus_size; i++) { + head = dbg_bus + i; + + writel_relaxed(0, mem_base + head->disable_bus_addr); + writel_relaxed(BIT(0), mem_base + MMSS_VBIF_TEST_BUS_OUT_CTRL); + /* make sure that other bus is off */ + wmb(); + + _sde_dbg_dump_vbif_debug_bus_entry(head, mem_base, dump_addr, + in_log); + if (dump_addr) + dump_addr += (head->block_cnt * head->test_pnt_cnt * 4); + } + + _sde_dbg_enable_power(false); + + dev_info(sde_dbg_base.dev, "======== end %s dump =========\n", + bus->cmn.name); +} + +/** + * _sde_dump_array - dump array of register bases + * @blk_arr: array of register base pointers + * @len: length of blk_arr + * @do_panic: whether to trigger a panic after dumping + * @name: string indicating origin of dump + * @dump_dbgbus_sde: whether to dump the sde debug bus + * @dump_dbgbus_vbif_rt: whether to dump the vbif rt debug bus + */ +static void _sde_dump_array(struct sde_dbg_reg_base *blk_arr[], + u32 len, bool do_panic, const char *name, bool dump_dbgbus_sde, + bool dump_dbgbus_vbif_rt, bool dump_all) +{ + int i; + + mutex_lock(&sde_dbg_base.mutex); + + for (i = 0; i < len; i++) { + if (blk_arr[i] != NULL) + _sde_dump_reg_by_ranges(blk_arr[i], + sde_dbg_base.enable_reg_dump); + } + + if (dump_all) + sde_evtlog_dump_all(sde_dbg_base.evtlog); + + if (dump_dbgbus_sde) + _sde_dbg_dump_sde_dbg_bus(&sde_dbg_base.dbgbus_sde); + + if (dump_dbgbus_vbif_rt) + _sde_dbg_dump_vbif_dbg_bus(&sde_dbg_base.dbgbus_vbif_rt); + + if (do_panic && sde_dbg_base.panic_on_err) + panic(name); + + mutex_unlock(&sde_dbg_base.mutex); +} + +/** + * _sde_dump_work - deferred dump work function + * @work: work structure + */ +static void _sde_dump_work(struct work_struct *work) +{ + _sde_dump_array(sde_dbg_base.req_dump_blks, + ARRAY_SIZE(sde_dbg_base.req_dump_blks), + sde_dbg_base.work_panic, "evtlog_workitem", + sde_dbg_base.dbgbus_sde.cmn.include_in_deferred_work, + sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work, + sde_dbg_base.dump_all); +} + +void sde_dbg_dump(bool queue_work, const char *name, ...) +{ + int i, index = 0; + bool do_panic = false; + bool dump_dbgbus_sde = false; + bool dump_dbgbus_vbif_rt = false; + bool dump_all = false; + va_list args; + char *blk_name = NULL; + struct sde_dbg_reg_base *blk_base = NULL; + struct sde_dbg_reg_base **blk_arr; + u32 blk_len; + + if (!sde_evtlog_is_enabled(sde_dbg_base.evtlog, SDE_EVTLOG_DEFAULT)) + return; + + if (queue_work && work_pending(&sde_dbg_base.dump_work)) + return; + + blk_arr = &sde_dbg_base.req_dump_blks[0]; + blk_len = ARRAY_SIZE(sde_dbg_base.req_dump_blks); + + memset(sde_dbg_base.req_dump_blks, 0, + sizeof(sde_dbg_base.req_dump_blks)); + sde_dbg_base.dump_all = false; + + va_start(args, name); + i = 0; + while ((blk_name = va_arg(args, char*))) { + if (i++ >= SDE_EVTLOG_MAX_DATA) { + pr_err("could not parse all dump arguments\n"); + break; + } + if (IS_ERR_OR_NULL(blk_name)) + break; + + blk_base = _sde_dump_get_blk_addr(blk_name); + if (blk_base) { + if (index < blk_len) { + blk_arr[index] = blk_base; + index++; + } else { + pr_err("insufficient space to to dump %s\n", + blk_name); + } + } + if (!strcmp(blk_name, "all")) + dump_all = true; + + if (!strcmp(blk_name, "dbg_bus")) + dump_dbgbus_sde = true; + + if (!strcmp(blk_name, "vbif_dbg_bus")) + dump_dbgbus_vbif_rt = true; + + if (!strcmp(blk_name, "panic")) + do_panic = true; + } + va_end(args); + + if (queue_work) { + /* schedule work to dump later */ + sde_dbg_base.work_panic = do_panic; + sde_dbg_base.dbgbus_sde.cmn.include_in_deferred_work = + dump_dbgbus_sde; + sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work = + dump_dbgbus_vbif_rt; + sde_dbg_base.dump_all = dump_all; + schedule_work(&sde_dbg_base.dump_work); + } else { + _sde_dump_array(blk_arr, blk_len, do_panic, name, + dump_dbgbus_sde, dump_dbgbus_vbif_rt, dump_all); + } +} + +void sde_dbg_ctrl(const char *name, ...) +{ + int i = 0; + va_list args; + char *blk_name = NULL; + + + /* no debugfs controlled events are enabled, just return */ + if (!sde_dbg_base.debugfs_ctrl) + return; + + va_start(args, name); + + while ((blk_name = va_arg(args, char*))) { + if (i++ >= SDE_EVTLOG_MAX_DATA) { + pr_err("could not parse all dbg arguments\n"); + break; + } + + if (IS_ERR_OR_NULL(blk_name)) + break; + + if (!strcmp(blk_name, "stop_ftrace") && + sde_dbg_base.debugfs_ctrl & + DBG_CTRL_STOP_FTRACE) { + pr_debug("tracing off\n"); + tracing_off(); + } + + if (!strcmp(blk_name, "panic_underrun") && + sde_dbg_base.debugfs_ctrl & + DBG_CTRL_PANIC_UNDERRUN) { + pr_debug("panic underrun\n"); + panic("underrun"); + } + } + +} + +/* + * sde_dbg_debugfs_open - debugfs open handler for evtlog dump + * @inode: debugfs inode + * @file: file handle + */ +static int sde_dbg_debugfs_open(struct inode *inode, struct file *file) +{ + if (!inode || !file) + return -EINVAL; + + /* non-seekable */ + file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); + file->private_data = inode->i_private; + return 0; +} + +/** + * sde_evtlog_dump_read - debugfs read handler for evtlog dump + * @file: file handler + * @buff: user buffer content for debugfs + * @count: size of user buffer + * @ppos: position offset of user buffer + */ +static ssize_t sde_evtlog_dump_read(struct file *file, char __user *buff, + size_t count, loff_t *ppos) +{ + ssize_t len = 0; + char evtlog_buf[SDE_EVTLOG_BUF_MAX]; + + if (!buff || !ppos) + return -EINVAL; + + len = sde_evtlog_dump_to_buffer(sde_dbg_base.evtlog, evtlog_buf, + SDE_EVTLOG_BUF_MAX, true); + if (len < 0 || len > count) { + pr_err("len is more than user buffer size"); + return 0; + } + + if (copy_to_user(buff, evtlog_buf, len)) + return -EFAULT; + *ppos += len; + + return len; +} + +/** + * sde_evtlog_dump_write - debugfs write handler for evtlog dump + * @file: file handler + * @user_buf: user buffer content from debugfs + * @count: size of user buffer + * @ppos: position offset of user buffer + */ +static ssize_t sde_evtlog_dump_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + _sde_dump_reg_all(); + + sde_evtlog_dump_all(sde_dbg_base.evtlog); + + _sde_dbg_dump_sde_dbg_bus(&sde_dbg_base.dbgbus_sde); + _sde_dbg_dump_vbif_dbg_bus(&sde_dbg_base.dbgbus_vbif_rt); + + if (sde_dbg_base.panic_on_err) + panic("sde"); + + return count; +} + +static const struct file_operations sde_evtlog_fops = { + .open = sde_dbg_debugfs_open, + .read = sde_evtlog_dump_read, + .write = sde_evtlog_dump_write, +}; + +/** + * sde_dbg_ctrl_read - debugfs read handler for debug ctrl read + * @file: file handler + * @buff: user buffer content for debugfs + * @count: size of user buffer + * @ppos: position offset of user buffer + */ +static ssize_t sde_dbg_ctrl_read(struct file *file, char __user *buff, + size_t count, loff_t *ppos) +{ + ssize_t len = 0; + char buf[24] = {'\0'}; + + if (!buff || !ppos) + return -EINVAL; + + if (*ppos) + return 0; /* the end */ + + len = snprintf(buf, sizeof(buf), "0x%x\n", sde_dbg_base.debugfs_ctrl); + pr_debug("%s: ctrl:0x%x len:0x%zx\n", + __func__, sde_dbg_base.debugfs_ctrl, len); + + if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) { + pr_err("error copying the buffer! count:0x%zx\n", count); + return -EFAULT; + } + + *ppos += len; /* increase offset */ + return len; +} + +/** + * sde_dbg_ctrl_write - debugfs read handler for debug ctrl write + * @file: file handler + * @user_buf: user buffer content from debugfs + * @count: size of user buffer + * @ppos: position offset of user buffer + */ +static ssize_t sde_dbg_ctrl_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + u32 dbg_ctrl = 0; + char buf[24]; + + if (!file) { + pr_err("DbgDbg: %s: error no file --\n", __func__); + return -EINVAL; + } + + if (count >= sizeof(buf)) + return -EFAULT; + + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + buf[count] = 0; /* end of string */ + + if (kstrtouint(buf, 0, &dbg_ctrl)) { + pr_err("%s: error in the number of bytes\n", __func__); + return -EFAULT; + } + + pr_debug("dbg_ctrl_read:0x%x\n", dbg_ctrl); + sde_dbg_base.debugfs_ctrl = dbg_ctrl; + + return count; +} + +static const struct file_operations sde_dbg_ctrl_fops = { + .open = sde_dbg_debugfs_open, + .read = sde_dbg_ctrl_read, + .write = sde_dbg_ctrl_write, +}; + +void sde_dbg_init_dbg_buses(u32 hwversion) +{ + static struct sde_dbg_base *dbg = &sde_dbg_base; + char debug_name[80] = ""; + + memset(&dbg->dbgbus_sde, 0, sizeof(dbg->dbgbus_sde)); + memset(&dbg->dbgbus_vbif_rt, 0, sizeof(dbg->dbgbus_vbif_rt)); + + switch (hwversion) { + case SDE_HW_VER_300: + case SDE_HW_VER_301: + dbg->dbgbus_sde.entries = dbg_bus_sde_8998; + dbg->dbgbus_sde.cmn.entries_size = ARRAY_SIZE(dbg_bus_sde_8998); + dbg->dbgbus_sde.cmn.flags = DBGBUS_FLAGS_DSPP; + + dbg->dbgbus_vbif_rt.entries = vbif_dbg_bus_msm8998; + dbg->dbgbus_vbif_rt.cmn.entries_size = + ARRAY_SIZE(vbif_dbg_bus_msm8998); + break; + default: + pr_err("unsupported chipset id %u\n", hwversion); + break; + } + + if (dbg->dbgbus_sde.entries) { + dbg->dbgbus_sde.cmn.name = DBGBUS_NAME_SDE; + snprintf(debug_name, sizeof(debug_name), "%s_dbgbus", + dbg->dbgbus_sde.cmn.name); + dbg->dbgbus_sde.cmn.enable_mask = DEFAULT_DBGBUS_SDE; + debugfs_create_u32(debug_name, 0600, dbg->root, + &dbg->dbgbus_sde.cmn.enable_mask); + } + + if (dbg->dbgbus_vbif_rt.entries) { + dbg->dbgbus_vbif_rt.cmn.name = DBGBUS_NAME_VBIF_RT; + snprintf(debug_name, sizeof(debug_name), "%s_dbgbus", + dbg->dbgbus_vbif_rt.cmn.name); + dbg->dbgbus_vbif_rt.cmn.enable_mask = DEFAULT_DBGBUS_VBIFRT; + debugfs_create_u32(debug_name, 0600, dbg->root, + &dbg->dbgbus_vbif_rt.cmn.enable_mask); + } +} + +int sde_dbg_init(struct dentry *debugfs_root, struct device *dev, + struct sde_dbg_power_ctrl *power_ctrl) +{ + int i; + + mutex_init(&sde_dbg_base.mutex); + INIT_LIST_HEAD(&sde_dbg_base.reg_base_list); + sde_dbg_base.dev = dev; + sde_dbg_base.power_ctrl = *power_ctrl; + + + sde_dbg_base.evtlog = sde_evtlog_init(); + if (IS_ERR_OR_NULL(sde_dbg_base.evtlog)) + return PTR_ERR(sde_dbg_base.evtlog); + + sde_dbg_base_evtlog = sde_dbg_base.evtlog; + + sde_dbg_base.root = debugfs_create_dir("evt_dbg", debugfs_root); + if (IS_ERR_OR_NULL(sde_dbg_base.root)) { + pr_err("debugfs_create_dir fail, error %ld\n", + PTR_ERR(sde_dbg_base.root)); + sde_dbg_base.root = NULL; + return -ENODEV; + } + + INIT_WORK(&sde_dbg_base.dump_work, _sde_dump_work); + sde_dbg_base.work_panic = false; + + for (i = 0; i < SDE_EVTLOG_ENTRY; i++) + sde_dbg_base.evtlog->logs[i].counter = i; + + debugfs_create_file("dbg_ctrl", 0600, sde_dbg_base.root, NULL, + &sde_dbg_ctrl_fops); + debugfs_create_file("dump", 0600, sde_dbg_base.root, NULL, + &sde_evtlog_fops); + debugfs_create_u32("enable", 0600, sde_dbg_base.root, + &(sde_dbg_base.evtlog->enable)); + debugfs_create_u32("panic", 0600, sde_dbg_base.root, + &sde_dbg_base.panic_on_err); + debugfs_create_u32("reg_dump", 0600, sde_dbg_base.root, + &sde_dbg_base.enable_reg_dump); + + sde_dbg_base.panic_on_err = DEFAULT_PANIC; + sde_dbg_base.enable_reg_dump = DEFAULT_REGDUMP; + + pr_info("evtlog_status: enable:%d, panic:%d, dump:%d\n", + sde_dbg_base.evtlog->enable, sde_dbg_base.panic_on_err, + sde_dbg_base.enable_reg_dump); + + return 0; +} + +/** + * sde_dbg_destroy - destroy sde debug facilities + */ +void sde_dbg_destroy(void) +{ + debugfs_remove_recursive(sde_dbg_base.root); + sde_dbg_base.root = NULL; + + sde_dbg_base_evtlog = NULL; + sde_evtlog_destroy(sde_dbg_base.evtlog); + sde_dbg_base.evtlog = NULL; + mutex_destroy(&sde_dbg_base.mutex); +} + +/** + * sde_dbg_reg_base_release - release allocated reg dump file private data + * @inode: debugfs inode + * @file: file handle + * @Return: 0 on success + */ +static int sde_dbg_reg_base_release(struct inode *inode, struct file *file) +{ + struct sde_dbg_reg_base *dbg; + + if (!file) + return -EINVAL; + + dbg = file->private_data; + if (!dbg) + return -ENODEV; + + mutex_lock(&sde_dbg_base.mutex); + if (dbg && dbg->buf) { + kfree(dbg->buf); + dbg->buf_len = 0; + dbg->buf = NULL; + } + mutex_unlock(&sde_dbg_base.mutex); + + return 0; +} + + +/** + * sde_dbg_reg_base_offset_write - set new offset and len to debugfs reg base + * @file: file handler + * @user_buf: user buffer content from debugfs + * @count: size of user buffer + * @ppos: position offset of user buffer + */ +static ssize_t sde_dbg_reg_base_offset_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct sde_dbg_reg_base *dbg; + u32 off = 0; + u32 cnt = DEFAULT_BASE_REG_CNT; + char buf[24]; + ssize_t rc = count; + + if (!file) + return -EINVAL; + + dbg = file->private_data; + if (!dbg) + return -ENODEV; + + if (count >= sizeof(buf)) + return -EFAULT; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + buf[count] = 0; /* end of string */ + + if (sscanf(buf, "%5x %x", &off, &cnt) != 2) + return -EFAULT; + + mutex_lock(&sde_dbg_base.mutex); + if (off > dbg->max_offset) { + rc = -EINVAL; + goto exit; + } + + if (off % sizeof(u32)) { + rc = -EINVAL; + goto exit; + } + + if (cnt > (dbg->max_offset - off)) + cnt = dbg->max_offset - off; + + if (cnt % sizeof(u32)) { + rc = -EINVAL; + goto exit; + } + + if (cnt == 0) + return -EINVAL; + + dbg->off = off; + dbg->cnt = cnt; + +exit: + mutex_unlock(&sde_dbg_base.mutex); + pr_debug("offset=%x cnt=%x\n", off, cnt); + + return rc; +} + +/** + * sde_dbg_reg_base_offset_read - read current offset and len of register base + * @file: file handler + * @user_buf: user buffer content from debugfs + * @count: size of user buffer + * @ppos: position offset of user buffer + */ +static ssize_t sde_dbg_reg_base_offset_read(struct file *file, + char __user *buff, size_t count, loff_t *ppos) +{ + struct sde_dbg_reg_base *dbg; + int len = 0; + char buf[24] = {'\0'}; + + if (!file) + return -EINVAL; + + dbg = file->private_data; + if (!dbg) + return -ENODEV; + + if (!ppos) + return -EINVAL; + + if (*ppos) + return 0; /* the end */ + + mutex_lock(&sde_dbg_base.mutex); + if (dbg->off % sizeof(u32)) { + mutex_unlock(&sde_dbg_base.mutex); + return -EFAULT; + } + + len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt); + if (len < 0 || len >= sizeof(buf)) { + mutex_unlock(&sde_dbg_base.mutex); + return 0; + } + + if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) { + mutex_unlock(&sde_dbg_base.mutex); + return -EFAULT; + } + + *ppos += len; /* increase offset */ + mutex_unlock(&sde_dbg_base.mutex); + + return len; +} + +/** + * sde_dbg_reg_base_reg_write - write to reg base hw at offset a given value + * @file: file handler + * @user_buf: user buffer content from debugfs + * @count: size of user buffer + * @ppos: position offset of user buffer + */ +static ssize_t sde_dbg_reg_base_reg_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct sde_dbg_reg_base *dbg; + size_t off; + u32 data, cnt; + char buf[24]; + + if (!file) + return -EINVAL; + + dbg = file->private_data; + if (!dbg) + return -ENODEV; + + if (count >= sizeof(buf)) + return -EFAULT; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + buf[count] = 0; /* end of string */ + + cnt = sscanf(buf, "%zx %x", &off, &data); + + if (cnt < 2) + return -EFAULT; + + mutex_lock(&sde_dbg_base.mutex); + if (off >= dbg->max_offset) { + mutex_unlock(&sde_dbg_base.mutex); + return -EFAULT; + } + + _sde_dbg_enable_power(true); + + writel_relaxed(data, dbg->base + off); + + _sde_dbg_enable_power(false); + + mutex_unlock(&sde_dbg_base.mutex); + + pr_debug("addr=%zx data=%x\n", off, data); + + return count; +} + +/** + * sde_dbg_reg_base_reg_read - read len from reg base hw at current offset + * @file: file handler + * @user_buf: user buffer content from debugfs + * @count: size of user buffer + * @ppos: position offset of user buffer + */ +static ssize_t sde_dbg_reg_base_reg_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct sde_dbg_reg_base *dbg; + size_t len; + + if (!file) + return -EINVAL; + + dbg = file->private_data; + if (!dbg) { + pr_err("invalid handle\n"); + return -ENODEV; + } + + if (!ppos) + return -EINVAL; + + mutex_lock(&sde_dbg_base.mutex); + if (!dbg->buf) { + char *hwbuf, *hwbuf_cur; + char dump_buf[64]; + char __iomem *ioptr; + int cnt, tot; + + dbg->buf_len = sizeof(dump_buf) * + DIV_ROUND_UP(dbg->cnt, ROW_BYTES); + + if (dbg->buf_len % sizeof(u32)) + return -EINVAL; + + dbg->buf = kzalloc(dbg->buf_len, GFP_KERNEL); + + if (!dbg->buf) { + mutex_unlock(&sde_dbg_base.mutex); + return -ENOMEM; + } + + hwbuf = kzalloc(dbg->buf_len, GFP_KERNEL); + if (!hwbuf) { + kfree(dbg->buf); + mutex_unlock(&sde_dbg_base.mutex); + return -ENOMEM; + } + hwbuf_cur = hwbuf; + + ioptr = dbg->base + dbg->off; + tot = 0; + + _sde_dbg_enable_power(true); + + memcpy_fromio(hwbuf, ioptr, dbg->buf_len); + + _sde_dbg_enable_power(false); + + for (cnt = dbg->cnt; cnt > 0; cnt -= ROW_BYTES) { + hex_dump_to_buffer(hwbuf_cur, + min(cnt, ROW_BYTES), + ROW_BYTES, GROUP_BYTES, dump_buf, + sizeof(dump_buf), false); + len = scnprintf(dbg->buf + tot, dbg->buf_len - tot, + "0x%08x: %s\n", + ((int) (unsigned long) hwbuf_cur) - + ((int) (unsigned long) dbg->base), + dump_buf); + + hwbuf_cur += ROW_BYTES; + tot += len; + if (tot >= dbg->buf_len) + break; + } + + dbg->buf_len = tot; + kfree(hwbuf); + } + + if (*ppos >= dbg->buf_len) { + mutex_unlock(&sde_dbg_base.mutex); + return 0; /* done reading */ + } + + len = min(count, dbg->buf_len - (size_t) *ppos); + if (copy_to_user(user_buf, dbg->buf + *ppos, len)) { + mutex_unlock(&sde_dbg_base.mutex); + pr_err("failed to copy to user\n"); + return -EFAULT; + } + + *ppos += len; /* increase offset */ + mutex_unlock(&sde_dbg_base.mutex); + + return len; +} + +static const struct file_operations sde_off_fops = { + .open = sde_dbg_debugfs_open, + .release = sde_dbg_reg_base_release, + .read = sde_dbg_reg_base_offset_read, + .write = sde_dbg_reg_base_offset_write, +}; + +static const struct file_operations sde_reg_fops = { + .open = sde_dbg_debugfs_open, + .release = sde_dbg_reg_base_release, + .read = sde_dbg_reg_base_reg_read, + .write = sde_dbg_reg_base_reg_write, +}; + +int sde_dbg_reg_register_base(const char *name, void __iomem *base, + size_t max_offset) +{ + struct sde_dbg_base *dbg_base = &sde_dbg_base; + struct sde_dbg_reg_base *reg_base; + struct dentry *ent_off, *ent_reg; + char dn[80] = ""; + int prefix_len = 0; + + reg_base = kzalloc(sizeof(*reg_base), GFP_KERNEL); + if (!reg_base) + return -ENOMEM; + + if (name) + strlcpy(reg_base->name, name, sizeof(reg_base->name)); + reg_base->base = base; + reg_base->max_offset = max_offset; + reg_base->off = 0; + reg_base->cnt = DEFAULT_BASE_REG_CNT; + reg_base->reg_dump = NULL; + + if (name) + prefix_len = snprintf(dn, sizeof(dn), "%s_", name); + strlcpy(dn + prefix_len, "off", sizeof(dn) - prefix_len); + ent_off = debugfs_create_file(dn, 0600, dbg_base->root, reg_base, + &sde_off_fops); + if (IS_ERR_OR_NULL(ent_off)) { + pr_err("debugfs_create_file: offset fail\n"); + goto off_fail; + } + + strlcpy(dn + prefix_len, "reg", sizeof(dn) - prefix_len); + ent_reg = debugfs_create_file(dn, 0600, dbg_base->root, reg_base, + &sde_reg_fops); + if (IS_ERR_OR_NULL(ent_reg)) { + pr_err("debugfs_create_file: reg fail\n"); + goto reg_fail; + } + + /* Initialize list to make sure check for null list will be valid */ + INIT_LIST_HEAD(®_base->sub_range_list); + + pr_debug("%s base: %pK max_offset 0x%zX\n", reg_base->name, + reg_base->base, reg_base->max_offset); + + list_add(®_base->reg_base_head, &dbg_base->reg_base_list); + + return 0; +reg_fail: + debugfs_remove(ent_off); +off_fail: + kfree(reg_base); + return -ENODEV; +} + +void sde_dbg_reg_register_dump_range(const char *base_name, + const char *range_name, u32 offset_start, u32 offset_end, + uint32_t xin_id) +{ + struct sde_dbg_reg_base *reg_base; + struct sde_dbg_reg_range *range; + + reg_base = _sde_dump_get_blk_addr(base_name); + if (!reg_base) { + pr_err("error: for range %s unable to locate base %s\n", + range_name, base_name); + return; + } + + if (!range_name || strlen(range_name) == 0) { + pr_err("%pS: bad range name, base_name %s, offset_start 0x%X, end 0x%X\n", + __builtin_return_address(0), base_name, + offset_start, offset_end); + return; + } + + if (offset_end - offset_start < REG_DUMP_ALIGN || + offset_start > offset_end) { + pr_err("%pS: bad range, base_name %s, range_name %s, offset_start 0x%X, end 0x%X\n", + __builtin_return_address(0), base_name, + range_name, offset_start, offset_end); + return; + } + + range = kzalloc(sizeof(*range), GFP_KERNEL); + if (!range) + return; + + strlcpy(range->range_name, range_name, sizeof(range->range_name)); + range->offset.start = offset_start; + range->offset.end = offset_end; + range->xin_id = xin_id; + list_add_tail(&range->head, ®_base->sub_range_list); + + pr_debug("base %s, range %s, start 0x%X, end 0x%X\n", + base_name, range->range_name, + range->offset.start, range->offset.end); +} + +void sde_dbg_set_sde_top_offset(u32 blk_off) +{ + sde_dbg_base.dbgbus_sde.top_blk_off = blk_off; +} diff --git a/drivers/gpu/drm/msm/sde_dbg.h b/drivers/gpu/drm/msm/sde_dbg.h index 271c41f05ce5..ce36cba08039 100644 --- a/drivers/gpu/drm/msm/sde_dbg.h +++ b/drivers/gpu/drm/msm/sde_dbg.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, 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 @@ -17,9 +17,10 @@ #include #include -#define SDE_EVTLOG_DATA_LIMITER (-1) +#define SDE_EVTLOG_DATA_LIMITER (0xC0DEBEEF) #define SDE_EVTLOG_FUNC_ENTRY 0x1111 #define SDE_EVTLOG_FUNC_EXIT 0x2222 +#define SDE_EVTLOG_ERROR 0xebad #define SDE_DBG_DUMP_DATA_LIMITER (NULL) @@ -29,34 +30,312 @@ enum sde_dbg_evtlog_flag { SDE_EVTLOG_ALL = BIT(7) }; +enum sde_dbg_dump_flag { + SDE_DBG_DUMP_IN_LOG = BIT(0), + SDE_DBG_DUMP_IN_MEM = BIT(1), +}; + +#ifdef CONFIG_DRM_SDE_EVTLOG_DEBUG +#define SDE_EVTLOG_DEFAULT_ENABLE 1 +#else +#define SDE_EVTLOG_DEFAULT_ENABLE 0 +#endif + +/* + * evtlog will print this number of entries when it is called through + * sysfs node or panic. This prevents kernel log from evtlog message + * flood. + */ +#define SDE_EVTLOG_PRINT_ENTRY 256 + +/* + * evtlog keeps this number of entries in memory for debug purpose. This + * number must be greater than print entry to prevent out of bound evtlog + * entry array access. + */ +#define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 8) +#define SDE_EVTLOG_MAX_DATA 15 +#define SDE_EVTLOG_BUF_MAX 512 +#define SDE_EVTLOG_BUF_ALIGN 32 + +struct sde_dbg_power_ctrl { + void *handle; + void *client; + int (*enable_fn)(void *handle, void *client, bool enable); +}; + +struct sde_dbg_evtlog_log { + u32 counter; + s64 time; + const char *name; + int line; + u32 data[SDE_EVTLOG_MAX_DATA]; + u32 data_cnt; + int pid; +}; + +struct sde_dbg_evtlog { + struct sde_dbg_evtlog_log logs[SDE_EVTLOG_ENTRY]; + u32 first; + u32 last; + u32 last_dump; + u32 curr; + u32 next; + u32 enable; + spinlock_t spin_lock; +}; + +extern struct sde_dbg_evtlog *sde_dbg_base_evtlog; + /** - * SDE_EVT32 - Write an list of 32bit values as an event into the event log + * SDE_EVT32 - Write a list of 32bit values to the event log, default area * ... - variable arguments */ -#define SDE_EVT32(...) sde_evtlog(__func__, __LINE__, SDE_EVTLOG_DEFAULT, \ - ##__VA_ARGS__, SDE_EVTLOG_DATA_LIMITER) -#define SDE_EVT32_IRQ(...) sde_evtlog(__func__, __LINE__, SDE_EVTLOG_IRQ, \ - ##__VA_ARGS__, SDE_EVTLOG_DATA_LIMITER) +#define SDE_EVT32(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \ + __LINE__, SDE_EVTLOG_DEFAULT, ##__VA_ARGS__, \ + SDE_EVTLOG_DATA_LIMITER) + +/** + * SDE_EVT32_IRQ - Write a list of 32bit values to the event log, IRQ area + * ... - variable arguments + */ +#define SDE_EVT32_IRQ(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \ + __LINE__, SDE_EVTLOG_IRQ, ##__VA_ARGS__, \ + SDE_EVTLOG_DATA_LIMITER) + +/** + * SDE_DBG_DUMP - trigger dumping of all sde_dbg facilities + * @va_args: list of named register dump ranges and regions to dump, as + * registered previously through sde_dbg_reg_register_base and + * sde_dbg_reg_register_dump_range. + * Including the special name "panic" will trigger a panic after + * the dumping work has completed. + */ +#define SDE_DBG_DUMP(...) sde_dbg_dump(false, __func__, ##__VA_ARGS__, \ + SDE_DBG_DUMP_DATA_LIMITER) -#define SDE_DBG_DUMP(...) \ - sde_dbg_dump(false, __func__, ##__VA_ARGS__, \ +/** + * SDE_DBG_DUMP_WQ - trigger dumping of all sde_dbg facilities, queuing the work + * @va_args: list of named register dump ranges and regions to dump, as + * registered previously through sde_dbg_reg_register_base and + * sde_dbg_reg_register_dump_range. + * Including the special name "panic" will trigger a panic after + * the dumping work has completed. + */ +#define SDE_DBG_DUMP_WQ(...) sde_dbg_dump(true, __func__, ##__VA_ARGS__, \ SDE_DBG_DUMP_DATA_LIMITER) -#define SDE_DBG_DUMP_WQ(...) \ - sde_dbg_dump(true, __func__, ##__VA_ARGS__, \ +/** + * SDE_DBG_EVT_CTRL - trigger a different driver events + * event: event that trigger different behavior in the driver + */ +#define SDE_DBG_CTRL(...) sde_dbg_ctrl(__func__, ##__VA_ARGS__, \ SDE_DBG_DUMP_DATA_LIMITER) #if defined(CONFIG_DEBUG_FS) -int sde_evtlog_init(struct dentry *debugfs_root); -void sde_evtlog_destroy(void); -void sde_evtlog(const char *name, int line, int flag, ...); -void sde_dbg_dump(bool queue, const char *name, ...); +/** + * sde_evtlog_init - allocate a new event log object + * Returns: evtlog or -ERROR + */ +struct sde_dbg_evtlog *sde_evtlog_init(void); + +/** + * sde_evtlog_destroy - destroy previously allocated event log + * @evtlog: pointer to evtlog + * Returns: none + */ +void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog); + +/** + * sde_evtlog_log - log an entry into the event log. + * log collection may be enabled/disabled entirely via debugfs + * log area collection may be filtered by user provided flags via debugfs. + * @evtlog: pointer to evtlog + * @name: function name of call site + * @line: line number of call site + * @flag: log area filter flag checked against user's debugfs request + * Returns: none + */ +void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, const char *name, int line, + int flag, ...); + +/** + * sde_evtlog_dump_all - print all entries in event log to kernel log + * @evtlog: pointer to evtlog + * Returns: none + */ +void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog); + +/** + * sde_evtlog_is_enabled - check whether log collection is enabled for given + * event log and log area flag + * @evtlog: pointer to evtlog + * @flag: log area filter flag + * Returns: none + */ +bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag); + +/** + * sde_evtlog_dump_to_buffer - print content of event log to the given buffer + * @evtlog: pointer to evtlog + * @evtlog_buf: target buffer to print into + * @evtlog_buf_size: size of target buffer + * @update_last_entry:» whether or not to stop at most recent entry + * Returns: number of bytes written to buffer + */ +ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog, + char *evtlog_buf, ssize_t evtlog_buf_size, + bool update_last_entry); + +/** + * sde_dbg_init_dbg_buses - initialize debug bus dumping support for the chipset + * @hwversion: Chipset revision + */ +void sde_dbg_init_dbg_buses(u32 hwversion); + +/** + * sde_dbg_init - initialize global sde debug facilities: evtlog, regdump + * @debugfs_root: debugfs root in which to create sde debug entries + * @dev: device handle + * @power_ctrl: power control callback structure for enabling clocks + * during register dumping + * Returns: 0 or -ERROR + */ +int sde_dbg_init(struct dentry *debugfs_root, struct device *dev, + struct sde_dbg_power_ctrl *power_ctrl); + +/** + * sde_dbg_destroy - destroy the global sde debug facilities + * Returns: none + */ +void sde_dbg_destroy(void); + +/** + * sde_dbg_dump - trigger dumping of all sde_dbg facilities + * @queue_work: whether to queue the dumping work to the work_struct + * @name: string indicating origin of dump + * @va_args: list of named register dump ranges and regions to dump, as + * registered previously through sde_dbg_reg_register_base and + * sde_dbg_reg_register_dump_range. + * Including the special name "panic" will trigger a panic after + * the dumping work has completed. + * Returns: none + */ +void sde_dbg_dump(bool queue_work, const char *name, ...); + +/** + * sde_dbg_ctrl - trigger specific actions for the driver with debugging + * purposes. Those actions need to be enabled by the debugfs entry + * so the driver executes those actions in the corresponding calls. + * @va_args: list of actions to trigger + * Returns: none + */ +void sde_dbg_ctrl(const char *name, ...); + +/** + * sde_dbg_reg_register_base - register a hw register address section for later + * dumping. call this before calling sde_dbg_reg_register_dump_range + * to be able to specify sub-ranges within the base hw range. + * @name: name of base region + * @base: base pointer of region + * @max_offset: length of region + * Returns: 0 or -ERROR + */ +int sde_dbg_reg_register_base(const char *name, void __iomem *base, + size_t max_offset); + +/** + * sde_dbg_reg_register_dump_range - register a hw register sub-region for + * later register dumping associated with base specified by + * sde_dbg_reg_register_base + * @base_name: name of base region + * @range_name: name of sub-range within base region + * @offset_start: sub-range's start offset from base's base pointer + * @offset_end: sub-range's end offset from base's base pointer + * @xin_id: xin id + * Returns: none + */ +void sde_dbg_reg_register_dump_range(const char *base_name, + const char *range_name, u32 offset_start, u32 offset_end, + uint32_t xin_id); + +/** + * sde_dbg_set_sde_top_offset - set the target specific offset from mdss base + * address of the top registers. Used for accessing debug bus controls. + * @blk_off: offset from mdss base of the top block + */ +void sde_dbg_set_sde_top_offset(u32 blk_off); #else -static inline int sde_evtlog_init(struct dentry *debugfs_root) { return 0; } -static inline void sde_evtlog(const char *name, int line, flag, ...) {} -static inline void sde_evtlog_destroy(void) { } -static inline void sde_dbg_dump(bool queue, const char *name, ...) {} -#endif +static inline struct sde_dbg_evtlog *sde_evtlog_init(void) +{ + return NULL; +} + +static inline void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog) +{ +} + +static inline void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, + const char *name, int line, int flag, ...) +{ +} + +static inline void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog) +{ +} + +static inline bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, + u32 flag) +{ + return false; +} + +static inline ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog, + char *evtlog_buf, ssize_t evtlog_buf_size, + bool update_last_entry) +{ + return 0; +} + +void sde_dbg_init_dbg_buses(u32 hwversion) +{ +} + +static inline int sde_dbg_init(struct dentry *debugfs_root, struct device *dev, + struct sde_dbg_power_ctrl *power_ctrl) +{ + return 0; +} + +static inline void sde_dbg_destroy(void) +{ +} + +static inline void sde_dbg_dump(bool queue_work, const char *name, ...) +{ +} + +static inline void sde_dbg_ctrl(const char *name, ...) +{ +} + +static inline int sde_dbg_reg_register_base(const char *name, + void __iomem *base, size_t max_offset) +{ + return 0; +} + +static inline void sde_dbg_reg_register_dump_range(const char *base_name, + const char *range_name, u32 offset_start, u32 offset_end, + uint32_t xin_id) +{ +} + +void sde_dbg_set_sde_top_offset(u32 blk_off) +{ +} +#endif /* defined(CONFIG_DEBUG_FS) */ + #endif /* SDE_DBG_H_ */ diff --git a/drivers/gpu/drm/msm/sde_dbg_evtlog.c b/drivers/gpu/drm/msm/sde_dbg_evtlog.c index 72832776659d..70ba127ceb08 100644 --- a/drivers/gpu/drm/msm/sde_dbg_evtlog.c +++ b/drivers/gpu/drm/msm/sde_dbg_evtlog.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, 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 @@ -10,7 +10,7 @@ * GNU General Public License for more details. */ -#define pr_fmt(fmt) "sde_evtlog:[%s] " fmt, __func__ +#define pr_fmt(fmt) "sde_dbg:[%s] " fmt, __func__ #include #include @@ -18,77 +18,36 @@ #include #include #include +#include #include "sde_dbg.h" #include "sde_trace.h" -#ifdef CONFIG_DRM_SDE_EVTLOG_DEBUG -#define SDE_EVTLOG_DEFAULT_ENABLE 1 -#else -#define SDE_EVTLOG_DEFAULT_ENABLE 0 -#endif - -#define SDE_DBG_DEFAULT_PANIC 1 - -/* - * evtlog will print this number of entries when it is called through - * sysfs node or panic. This prevents kernel log from evtlog message - * flood. - */ -#define SDE_EVTLOG_PRINT_ENTRY 256 - -/* - * evtlog keeps this number of entries in memory for debug purpose. This - * number must be greater than print entry to prevent out of bound evtlog - * entry array access. - */ -#define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 4) -#define SDE_EVTLOG_MAX_DATA 15 -#define SDE_EVTLOG_BUF_MAX 512 -#define SDE_EVTLOG_BUF_ALIGN 32 - -DEFINE_SPINLOCK(sde_evtloglock); - -struct tlog { - u32 counter; - s64 time; - const char *name; - int line; - u32 data[SDE_EVTLOG_MAX_DATA]; - u32 data_cnt; - int pid; -}; - -static struct sde_dbg_evtlog { - struct tlog logs[SDE_EVTLOG_ENTRY]; - u32 first; - u32 last; - u32 curr; - struct dentry *evtlog; - u32 evtlog_enable; - u32 panic_on_err; - struct work_struct evtlog_dump_work; - bool work_panic; -} sde_dbg_evtlog; - -static inline bool sde_evtlog_is_enabled(u32 flag) +bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag) { - return (flag & sde_dbg_evtlog.evtlog_enable) || - (flag == SDE_EVTLOG_ALL && sde_dbg_evtlog.evtlog_enable); + if (!evtlog) + return false; + + return (flag & evtlog->enable) || + (flag == SDE_EVTLOG_ALL && evtlog->enable); } -void sde_evtlog(const char *name, int line, int flag, ...) +void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, const char *name, int line, + int flag, ...) { unsigned long flags; int i, val = 0; va_list args; - struct tlog *log; + struct sde_dbg_evtlog_log *log; + + if (!evtlog) + return; - if (!sde_evtlog_is_enabled(flag)) + if (!sde_evtlog_is_enabled(evtlog, flag)) return; - spin_lock_irqsave(&sde_evtloglock, flags); - log = &sde_dbg_evtlog.logs[sde_dbg_evtlog.curr]; + spin_lock_irqsave(&evtlog->spin_lock, flags); + log = &evtlog->logs[evtlog->curr]; log->time = ktime_to_us(ktime_get()); log->name = name; log->line = line; @@ -106,64 +65,79 @@ void sde_evtlog(const char *name, int line, int flag, ...) } va_end(args); log->data_cnt = i; - sde_dbg_evtlog.curr = (sde_dbg_evtlog.curr + 1) % SDE_EVTLOG_ENTRY; - sde_dbg_evtlog.last++; + evtlog->curr = (evtlog->curr + 1) % SDE_EVTLOG_ENTRY; + evtlog->last++; trace_sde_evtlog(name, line, i > 0 ? log->data[0] : 0, i > 1 ? log->data[1] : 0); - spin_unlock_irqrestore(&sde_evtloglock, flags); + spin_unlock_irqrestore(&evtlog->spin_lock, flags); } /* always dump the last entries which are not dumped yet */ -static bool _sde_evtlog_dump_calc_range(void) +static bool _sde_evtlog_dump_calc_range(struct sde_dbg_evtlog *evtlog, + bool update_last_entry) { - static u32 next; bool need_dump = true; unsigned long flags; - struct sde_dbg_evtlog *evtlog = &sde_dbg_evtlog; - spin_lock_irqsave(&sde_evtloglock, flags); + if (!evtlog) + return false; + + spin_lock_irqsave(&evtlog->spin_lock, flags); - evtlog->first = next; + evtlog->first = evtlog->next; - if (evtlog->last == evtlog->first) { + if (update_last_entry) + evtlog->last_dump = evtlog->last; + + if (evtlog->last_dump == evtlog->first) { need_dump = false; goto dump_exit; } - if (evtlog->last < evtlog->first) { + if (evtlog->last_dump < evtlog->first) { evtlog->first %= SDE_EVTLOG_ENTRY; - if (evtlog->last < evtlog->first) - evtlog->last += SDE_EVTLOG_ENTRY; + if (evtlog->last_dump < evtlog->first) + evtlog->last_dump += SDE_EVTLOG_ENTRY; } - if ((evtlog->last - evtlog->first) > SDE_EVTLOG_PRINT_ENTRY) { - pr_warn("evtlog buffer overflow before dump: %d\n", - evtlog->last - evtlog->first); - evtlog->first = evtlog->last - SDE_EVTLOG_PRINT_ENTRY; + if ((evtlog->last_dump - evtlog->first) > SDE_EVTLOG_PRINT_ENTRY) { + pr_info("evtlog skipping %d entries, last=%d\n", + evtlog->last_dump - evtlog->first - + SDE_EVTLOG_PRINT_ENTRY, + evtlog->last_dump - 1); + evtlog->first = evtlog->last_dump - SDE_EVTLOG_PRINT_ENTRY; } - next = evtlog->first + 1; + evtlog->next = evtlog->first + 1; dump_exit: - spin_unlock_irqrestore(&sde_evtloglock, flags); + spin_unlock_irqrestore(&evtlog->spin_lock, flags); return need_dump; } -static ssize_t sde_evtlog_dump_entry(char *evtlog_buf, ssize_t evtlog_buf_size) +ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog, + char *evtlog_buf, ssize_t evtlog_buf_size, + bool update_last_entry) { int i; ssize_t off = 0; - struct tlog *log, *prev_log; + struct sde_dbg_evtlog_log *log, *prev_log; unsigned long flags; - spin_lock_irqsave(&sde_evtloglock, flags); + if (!evtlog || !evtlog_buf) + return 0; - log = &sde_dbg_evtlog.logs[sde_dbg_evtlog.first % - SDE_EVTLOG_ENTRY]; + /* update markers, exit if nothing to print */ + if (!_sde_evtlog_dump_calc_range(evtlog, update_last_entry)) + return 0; - prev_log = &sde_dbg_evtlog.logs[(sde_dbg_evtlog.first - 1) % + spin_lock_irqsave(&evtlog->spin_lock, flags); + + log = &evtlog->logs[evtlog->first % SDE_EVTLOG_ENTRY]; + + prev_log = &evtlog->logs[(evtlog->first - 1) % SDE_EVTLOG_ENTRY]; off = snprintf((evtlog_buf + off), (evtlog_buf_size - off), "%s:%-4d", @@ -175,7 +149,7 @@ static ssize_t sde_evtlog_dump_entry(char *evtlog_buf, ssize_t evtlog_buf_size) } off += snprintf((evtlog_buf + off), (evtlog_buf_size - off), - "=>[%-8d:%-11llu:%9llu][%-4d]:", sde_dbg_evtlog.first, + "=>[%-8d:%-11llu:%9llu][%-4d]:", evtlog->first, log->time, (log->time - prev_log->time), log->pid); for (i = 0; i < log->data_cnt; i++) @@ -184,143 +158,41 @@ static ssize_t sde_evtlog_dump_entry(char *evtlog_buf, ssize_t evtlog_buf_size) off += snprintf((evtlog_buf + off), (evtlog_buf_size - off), "\n"); - spin_unlock_irqrestore(&sde_evtloglock, flags); + spin_unlock_irqrestore(&evtlog->spin_lock, flags); return off; } -static void _sde_evtlog_dump_all(void) -{ - char evtlog_buf[SDE_EVTLOG_BUF_MAX]; - - while (_sde_evtlog_dump_calc_range()) { - sde_evtlog_dump_entry(evtlog_buf, SDE_EVTLOG_BUF_MAX); - pr_info("%s", evtlog_buf); - } -} - -static void _sde_dump_array(bool dead, const char *name) -{ - _sde_evtlog_dump_all(); - - if (dead && sde_dbg_evtlog.panic_on_err) - panic(name); -} - -static void _sde_dump_work(struct work_struct *work) +void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog) { - _sde_dump_array(sde_dbg_evtlog.work_panic, "evtlog_workitem"); -} + char buf[SDE_EVTLOG_BUF_MAX]; + bool update_last_entry = true; -void sde_dbg_dump(bool queue, const char *name, ...) -{ - int i; - bool dead = false; - va_list args; - char *blk_name = NULL; - - if (!sde_evtlog_is_enabled(SDE_EVTLOG_DEFAULT)) + if (!evtlog) return; - if (queue && work_pending(&sde_dbg_evtlog.evtlog_dump_work)) - return; - - va_start(args, name); - for (i = 0; i < SDE_EVTLOG_MAX_DATA; i++) { - blk_name = va_arg(args, char*); - if (IS_ERR_OR_NULL(blk_name)) - break; - - if (!strcmp(blk_name, "panic")) - dead = true; - } - va_end(args); - - if (queue) { - /* schedule work to dump later */ - sde_dbg_evtlog.work_panic = dead; - schedule_work(&sde_dbg_evtlog.evtlog_dump_work); - } else { - _sde_dump_array(dead, name); + while (sde_evtlog_dump_to_buffer(evtlog, buf, sizeof(buf), + update_last_entry)) { + pr_info("%s", buf); + update_last_entry = false; } } -static int sde_evtlog_dump_open(struct inode *inode, struct file *file) +struct sde_dbg_evtlog *sde_evtlog_init(void) { - /* non-seekable */ - file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE); - file->private_data = inode->i_private; - return 0; -} - -static ssize_t sde_evtlog_dump_read(struct file *file, char __user *buff, - size_t count, loff_t *ppos) -{ - ssize_t len = 0; - char evtlog_buf[SDE_EVTLOG_BUF_MAX]; - - if (_sde_evtlog_dump_calc_range()) { - len = sde_evtlog_dump_entry(evtlog_buf, SDE_EVTLOG_BUF_MAX); - if (copy_to_user(buff, evtlog_buf, len)) - return -EFAULT; - *ppos += len; - } - - return len; -} - -static ssize_t sde_evtlog_dump_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - _sde_evtlog_dump_all(); - - if (sde_dbg_evtlog.panic_on_err) - panic("sde"); - - return count; -} - -static const struct file_operations sde_evtlog_fops = { - .open = sde_evtlog_dump_open, - .read = sde_evtlog_dump_read, - .write = sde_evtlog_dump_write, -}; - -int sde_evtlog_init(struct dentry *debugfs_root) -{ - int i; - - sde_dbg_evtlog.evtlog = debugfs_create_dir("evt_dbg", debugfs_root); - if (IS_ERR_OR_NULL(sde_dbg_evtlog.evtlog)) { - pr_err("debugfs_create_dir fail, error %ld\n", - PTR_ERR(sde_dbg_evtlog.evtlog)); - sde_dbg_evtlog.evtlog = NULL; - return -ENODEV; - } - - INIT_WORK(&sde_dbg_evtlog.evtlog_dump_work, _sde_dump_work); - sde_dbg_evtlog.work_panic = false; - - for (i = 0; i < SDE_EVTLOG_ENTRY; i++) - sde_dbg_evtlog.logs[i].counter = i; - - debugfs_create_file("dump", 0644, sde_dbg_evtlog.evtlog, NULL, - &sde_evtlog_fops); - debugfs_create_u32("enable", 0644, sde_dbg_evtlog.evtlog, - &sde_dbg_evtlog.evtlog_enable); - debugfs_create_u32("panic", 0644, sde_dbg_evtlog.evtlog, - &sde_dbg_evtlog.panic_on_err); + struct sde_dbg_evtlog *evtlog; - sde_dbg_evtlog.evtlog_enable = SDE_EVTLOG_DEFAULT_ENABLE; - sde_dbg_evtlog.panic_on_err = SDE_DBG_DEFAULT_PANIC; + evtlog = kzalloc(sizeof(*evtlog), GFP_KERNEL); + if (!evtlog) + return ERR_PTR(-ENOMEM); - pr_info("evtlog_status: enable:%d, panic:%d\n", - sde_dbg_evtlog.evtlog_enable, sde_dbg_evtlog.panic_on_err); + spin_lock_init(&evtlog->spin_lock); + evtlog->enable = SDE_EVTLOG_DEFAULT_ENABLE; - return 0; + return evtlog; } -void sde_evtlog_destroy(void) +void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog) { - debugfs_remove(sde_dbg_evtlog.evtlog); + kfree(evtlog); } diff --git a/drivers/gpu/drm/msm/sde_edid_parser.c b/drivers/gpu/drm/msm/sde_edid_parser.c index 50667c5921a0..cceaf1c27716 100644 --- a/drivers/gpu/drm/msm/sde_edid_parser.c +++ b/drivers/gpu/drm/msm/sde_edid_parser.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 @@ -252,12 +252,13 @@ static void sde_edid_parse_Y420CMDB( struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl, const u8 *db) { - u32 offset = 0; u8 cmdb_len = 0; u8 svd_len = 0; const u8 *svd = NULL; - u32 i = 0, j = 0; + u32 i = 0; u32 video_format = 0; + u32 num_cmdb_svd = 0; + const u32 mult = 8; if (!edid_ctrl) { DEV_ERR("%s: edid_ctrl is NULL\n", __func__); @@ -271,8 +272,8 @@ const u8 *db) SDE_EDID_DEBUG("%s +\n", __func__); cmdb_len = db[0] & 0x1f; - /* Byte 3 to L+1 contain SVDs */ - offset += 2; + if (cmdb_len < 1) + return; svd = sde_edid_find_block(edid_ctrl->edid, VIDEO_DATA_BLOCK); @@ -282,21 +283,26 @@ const u8 *db) ++svd; } - for (i = 0; i < svd_len; i++, j++) { + if (cmdb_len == 1) + num_cmdb_svd = svd_len; + else { + num_cmdb_svd = (cmdb_len - 1) * mult; + if (num_cmdb_svd > svd_len) + num_cmdb_svd = svd_len; + } + + for (i = 0; i < num_cmdb_svd; i++) { video_format = *(svd + i) & 0x7F; - if (cmdb_len == 1) { - /* If cmdb_len is 1, it means all SVDs support YUV */ - sde_edid_set_y420_support(connector, video_format); - } else if (db[offset] & (1 << j)) { + /* + * If cmdb_len is 1, it means all SVDs support YUV + * Else, we check each byte of the cmdb bitmap bitwise + * and match those bits with the formats populated + * during the parsing of the Video Data Blocks. + * Refer to CTA 861-F section 7.5.11 YCBCR 4:2:0 Capability + * Map Data Block for more details on this. + */ + if (cmdb_len == 1 || (db[2 + i / mult] & (1 << (i % mult)))) sde_edid_set_y420_support(connector, video_format); - - if (j & 0x80) { - j = j/8; - offset++; - if (offset >= cmdb_len) - break; - } - } } SDE_EDID_DEBUG("%s -\n", __func__); @@ -360,6 +366,33 @@ struct drm_connector *connector, struct sde_edid_ctrl *edid_ctrl) else SDE_EDID_DEBUG("YCbCr420 CMDB is not present\n"); + /* + * As per HDMI 2.0 spec, a sink supporting any modes + * requiring more than 340Mhz clock rate should support + * SCDC as well. This is required because we need the SCDC + * channel to set the TMDS clock ratio. However in cases + * where the TV publishes such a mode in its list of modes + * but does not have SCDC support as per HDMI HFVSDB block + * remove RGB mode support from the flags. Currently, in + * the list of modes not having deep color support only RGB + * modes shall requre a clock of 340Mhz and above such as the + * 4K@60fps case. All other modes shall be YUV. + * Deep color case is handled separately while choosing the + * best mode in the _sde_hdmi_choose_best_format API where + * we enable deep color only if it satisfies both source and + * sink requirements. However, that API assumes that at least + * RGB mode is supported on the mode. Hence, it would be better + * to remove the format support flags while parsing the EDID + * itself if it doesn't satisfy the HDMI spec requirement. + */ + + list_for_each_entry(mode, &connector->probed_modes, head) { + if ((mode->clock > MIN_SCRAMBLER_REQ_RATE) && + !connector->scdc_present) { + mode->flags &= ~DRM_MODE_FLAG_SUPPORTS_RGB; + } + } + SDE_EDID_DEBUG("%s -\n", __func__); } diff --git a/drivers/gpu/drm/msm/sde_edid_parser.h b/drivers/gpu/drm/msm/sde_edid_parser.h index 59e3dceca33c..a913219aac50 100644 --- a/drivers/gpu/drm/msm/sde_edid_parser.h +++ b/drivers/gpu/drm/msm/sde_edid_parser.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 @@ -33,6 +33,8 @@ #define SDE_CEA_EXT 0x02 #define SDE_EXTENDED_TAG 0x07 +#define MIN_SCRAMBLER_REQ_RATE 340000 + #define SDE_DRM_MODE_FLAG_FMT_MASK (0x3 << 20) enum extended_data_block_types { diff --git a/drivers/gpu/msm/adreno-gpulist.h b/drivers/gpu/msm/adreno-gpulist.h index 4ec04001ae7e..bc3f794555a5 100644 --- a/drivers/gpu/msm/adreno-gpulist.h +++ b/drivers/gpu/msm/adreno-gpulist.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-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 @@ -299,6 +299,22 @@ static const struct adreno_gpu_core adreno_gpulist[] = { .num_protected_regs = 0x20, .busy_mask = 0xFFFFFFFE, }, + { + .gpurev = ADRENO_REV_A509, + .core = 5, + .major = 0, + .minor = 9, + .patchid = ANY_ID, + .features = ADRENO_PREEMPTION | ADRENO_64BIT | + ADRENO_CONTENT_PROTECTION | ADRENO_CPZ_RETENTION, + .pm4fw_name = "a530_pm4.fw", + .pfpfw_name = "a530_pfp.fw", + .zap_name = "a512_zap", + .gpudev = &adreno_a5xx_gpudev, + .gmem_size = (SZ_256K + SZ_16K), + .num_protected_regs = 0x20, + .busy_mask = 0xFFFFFFFE, + }, { .gpurev = ADRENO_REV_A508, .core = 5, diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c index 7cab049771de..45d433e77b0e 100644 --- a/drivers/gpu/msm/adreno.c +++ b/drivers/gpu/msm/adreno.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2002,2007-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 @@ -1290,7 +1290,7 @@ static void _set_secvid(struct kgsl_device *device) adreno_writereg64(adreno_dev, ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_BASE, ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_BASE_HI, - KGSL_IOMMU_SECURE_BASE); + KGSL_IOMMU_SECURE_BASE(&device->mmu)); adreno_writereg(adreno_dev, ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_SIZE, KGSL_IOMMU_SECURE_SIZE); @@ -1693,7 +1693,7 @@ static int adreno_getproperty(struct kgsl_device *device, * anything to mmap(). */ shadowprop.gpuaddr = - (unsigned int) device->memstore.gpuaddr; + (unsigned long)device->memstore.gpuaddr; shadowprop.size = device->memstore.size; /* GSL needs this to be set, even if it appears to be meaningless */ diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h index 4a0acdcf8844..2a0940bc3c37 100644 --- a/drivers/gpu/msm/adreno.h +++ b/drivers/gpu/msm/adreno.h @@ -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 @@ -170,6 +170,7 @@ enum adreno_gpurev { ADRENO_REV_A505 = 505, ADRENO_REV_A506 = 506, ADRENO_REV_A508 = 508, + ADRENO_REV_A509 = 509, ADRENO_REV_A510 = 510, ADRENO_REV_A512 = 512, ADRENO_REV_A530 = 530, @@ -1006,6 +1007,7 @@ static inline int adreno_is_a5xx(struct adreno_device *adreno_dev) ADRENO_TARGET(a505, ADRENO_REV_A505) ADRENO_TARGET(a506, ADRENO_REV_A506) ADRENO_TARGET(a508, ADRENO_REV_A508) +ADRENO_TARGET(a509, ADRENO_REV_A509) ADRENO_TARGET(a510, ADRENO_REV_A510) ADRENO_TARGET(a512, ADRENO_REV_A512) ADRENO_TARGET(a530, ADRENO_REV_A530) diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c index 78f74b883877..4daf1fad6ee1 100644 --- a/drivers/gpu/msm/adreno_a5xx.c +++ b/drivers/gpu/msm/adreno_a5xx.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 @@ -59,6 +59,7 @@ static const struct adreno_vbif_platform a5xx_vbif_platforms[] = { { adreno_is_a530, a530_vbif }, { adreno_is_a512, a540_vbif }, { adreno_is_a510, a530_vbif }, + { adreno_is_a509, a540_vbif }, { adreno_is_a508, a530_vbif }, { adreno_is_a505, a530_vbif }, { adreno_is_a506, a530_vbif }, @@ -161,6 +162,7 @@ static const struct { { adreno_is_a530, a530_efuse_speed_bin }, { adreno_is_a505, a530_efuse_speed_bin }, { adreno_is_a512, a530_efuse_speed_bin }, + { adreno_is_a509, a530_efuse_speed_bin }, { adreno_is_a508, a530_efuse_speed_bin }, }; @@ -201,7 +203,8 @@ static void a5xx_platform_setup(struct adreno_device *adreno_dev) gpudev->vbif_xin_halt_ctrl0_mask = A510_VBIF_XIN_HALT_CTRL0_MASK; } else if (adreno_is_a540(adreno_dev) || - adreno_is_a512(adreno_dev)) { + adreno_is_a512(adreno_dev) || + adreno_is_a509(adreno_dev)) { gpudev->snapshot_data->sect_sizes->cp_merciu = 1024; } @@ -539,7 +542,8 @@ static void a5xx_regulator_disable(struct adreno_device *adreno_dev) unsigned int reg; struct kgsl_device *device = KGSL_DEVICE(adreno_dev); - if (adreno_is_a512(adreno_dev) || adreno_is_a508(adreno_dev)) + if (adreno_is_a512(adreno_dev) || adreno_is_a509(adreno_dev) || + adreno_is_a508(adreno_dev)) return; /* If feature is not supported or not enabled */ @@ -1199,6 +1203,7 @@ static const struct { { adreno_is_a540, a540_hwcg_regs, ARRAY_SIZE(a540_hwcg_regs) }, { adreno_is_a530, a530_hwcg_regs, ARRAY_SIZE(a530_hwcg_regs) }, { adreno_is_a512, a512_hwcg_regs, ARRAY_SIZE(a512_hwcg_regs) }, + { adreno_is_a509, a512_hwcg_regs, ARRAY_SIZE(a512_hwcg_regs) }, { adreno_is_a510, a510_hwcg_regs, ARRAY_SIZE(a510_hwcg_regs) }, { adreno_is_a505, a50x_hwcg_regs, ARRAY_SIZE(a50x_hwcg_regs) }, { adreno_is_a506, a50x_hwcg_regs, ARRAY_SIZE(a50x_hwcg_regs) }, @@ -1376,31 +1381,27 @@ static int _execute_reg_sequence(struct adreno_device *adreno_dev, /* todo double check the reg writes */ while ((cur - opcode) < length) { - switch (cur[0]) { - /* Write a 32 bit value to a 64 bit reg */ - case 1: + if (cur[0] == 1 && (length - (cur - opcode) >= 4)) { + /* Write a 32 bit value to a 64 bit reg */ reg = cur[2]; reg = (reg << 32) | cur[1]; kgsl_regwrite(KGSL_DEVICE(adreno_dev), reg, cur[3]); cur += 4; - break; - /* Write a 64 bit value to a 64 bit reg */ - case 2: + } else if (cur[0] == 2 && (length - (cur - opcode) >= 5)) { + /* Write a 64 bit value to a 64 bit reg */ reg = cur[2]; reg = (reg << 32) | cur[1]; val = cur[4]; val = (val << 32) | cur[3]; kgsl_regwrite(KGSL_DEVICE(adreno_dev), reg, val); cur += 5; - break; - /* Delay for X usec */ - case 3: + } else if (cur[0] == 3 && (length - (cur - opcode) >= 2)) { + /* Delay for X usec */ udelay(cur[1]); cur += 2; - break; - default: + } else return -EINVAL; - } } + } return 0; } @@ -1655,7 +1656,7 @@ static void a5xx_clk_set_options(struct adreno_device *adreno_dev, { if (!adreno_is_a540(adreno_dev) && !adreno_is_a512(adreno_dev) && - !adreno_is_a508(adreno_dev)) + !adreno_is_a508(adreno_dev) && !adreno_is_a509(adreno_dev)) return; /* Handle clock settings for GFX PSCBCs */ @@ -1961,7 +1962,8 @@ static void a5xx_start(struct adreno_device *adreno_dev) kgsl_regwrite(device, A5XX_CP_MERCIU_SIZE, 0x20); kgsl_regwrite(device, A5XX_CP_ROQ_THRESHOLDS_2, 0x40000030); kgsl_regwrite(device, A5XX_CP_ROQ_THRESHOLDS_1, 0x20100D0A); - } else if (adreno_is_a540(adreno_dev) || adreno_is_a512(adreno_dev)) { + } else if (adreno_is_a540(adreno_dev) || adreno_is_a512(adreno_dev) || + adreno_is_a509(adreno_dev)) { kgsl_regwrite(device, A5XX_CP_MEQ_THRESHOLDS, 0x40); kgsl_regwrite(device, A5XX_CP_MERCIU_SIZE, 0x400); kgsl_regwrite(device, A5XX_CP_ROQ_THRESHOLDS_2, 0x80000060); @@ -1980,7 +1982,8 @@ static void a5xx_start(struct adreno_device *adreno_dev) if (adreno_is_a505_or_a506(adreno_dev) || adreno_is_a508(adreno_dev)) kgsl_regwrite(device, A5XX_PC_DBG_ECO_CNTL, (0x100 << 11 | 0x100 << 22)); - else if (adreno_is_a510(adreno_dev) || adreno_is_a512(adreno_dev)) + else if (adreno_is_a510(adreno_dev) || adreno_is_a512(adreno_dev) || + adreno_is_a509(adreno_dev)) kgsl_regwrite(device, A5XX_PC_DBG_ECO_CNTL, (0x200 << 11 | 0x200 << 22)); else @@ -2073,7 +2076,8 @@ static void a5xx_start(struct adreno_device *adreno_dev) kgsl_regwrite(device, A5XX_TPL1_MODE_CNTL, bit << 7); kgsl_regwrite(device, A5XX_RB_MODE_CNTL, bit << 1); if (adreno_is_a540(adreno_dev) || - adreno_is_a512(adreno_dev)) + adreno_is_a512(adreno_dev) || + adreno_is_a509(adreno_dev)) kgsl_regwrite(device, A5XX_UCHE_DBG_ECO_CNTL_2, bit); } @@ -2489,8 +2493,8 @@ static int a5xx_rb_start(struct adreno_device *adreno_dev, adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_CNTL, A5XX_CP_RB_CNTL_DEFAULT); - adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_BASE, - rb->buffer_desc.gpuaddr); + adreno_writereg64(adreno_dev, ADRENO_REG_CP_RB_BASE, + ADRENO_REG_CP_RB_BASE_HI, rb->buffer_desc.gpuaddr); ret = a5xx_microcode_load(adreno_dev); if (ret) diff --git a/drivers/gpu/msm/adreno_a5xx_snapshot.c b/drivers/gpu/msm/adreno_a5xx_snapshot.c index 496fc6a9248e..2ed716274176 100644 --- a/drivers/gpu/msm/adreno_a5xx_snapshot.c +++ b/drivers/gpu/msm/adreno_a5xx_snapshot.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 @@ -410,6 +410,15 @@ static const unsigned int a5xx_registers[] = { 0xEC00, 0xEC05, 0xEC08, 0xECE9, 0xECF0, 0xECF0, /* VPC CTX 1 */ 0xEA80, 0xEA80, 0xEA82, 0xEAA3, 0xEAA5, 0xEAC2, +}; + +/* + * GPMU registers to dump for A5XX on snapshot. + * Registers in pairs - first value is the start offset, second + * is the stop offset (inclusive) + */ + +static const unsigned int a5xx_gpmu_registers[] = { /* GPMU */ 0xA800, 0xA8FF, 0xAC60, 0xAC60, }; @@ -661,24 +670,23 @@ static size_t a5xx_snapshot_pre_crashdump_regs(struct kgsl_device *device, return kgsl_snapshot_dump_registers(device, buf, remain, &pre_cdregs); } +struct registers { + const unsigned int *regs; + size_t size; +}; + static size_t a5xx_legacy_snapshot_registers(struct kgsl_device *device, - u8 *buf, size_t remain) + u8 *buf, size_t remain, const unsigned int *regs, size_t size) { - struct kgsl_snapshot_registers regs = { - .regs = a5xx_registers, - .count = ARRAY_SIZE(a5xx_registers) / 2, + struct kgsl_snapshot_registers snapshot_regs = { + .regs = regs, + .count = size / 2, }; - return kgsl_snapshot_dump_registers(device, buf, remain, ®s); + return kgsl_snapshot_dump_registers(device, buf, remain, + &snapshot_regs); } -static struct cdregs { - const unsigned int *regs; - unsigned int size; -} _a5xx_cd_registers[] = { - { a5xx_registers, ARRAY_SIZE(a5xx_registers) }, -}; - #define REG_PAIR_COUNT(_a, _i) \ (((_a)[(2 * (_i)) + 1] - (_a)[2 * (_i)]) + 1) @@ -688,11 +696,13 @@ static size_t a5xx_snapshot_registers(struct kgsl_device *device, u8 *buf, struct kgsl_snapshot_regs *header = (struct kgsl_snapshot_regs *)buf; unsigned int *data = (unsigned int *)(buf + sizeof(*header)); unsigned int *src = (unsigned int *) registers.hostptr; - unsigned int i, j, k; + struct registers *regs = (struct registers *)priv; + unsigned int j, k; unsigned int count = 0; if (crash_dump_valid == false) - return a5xx_legacy_snapshot_registers(device, buf, remain); + return a5xx_legacy_snapshot_registers(device, buf, remain, + regs->regs, regs->size); if (remain < sizeof(*header)) { SNAPSHOT_ERR_NOMEM(device, "REGISTERS"); @@ -701,24 +711,20 @@ static size_t a5xx_snapshot_registers(struct kgsl_device *device, u8 *buf, remain -= sizeof(*header); - for (i = 0; i < ARRAY_SIZE(_a5xx_cd_registers); i++) { - struct cdregs *regs = &_a5xx_cd_registers[i]; + for (j = 0; j < regs->size / 2; j++) { + unsigned int start = regs->regs[2 * j]; + unsigned int end = regs->regs[(2 * j) + 1]; - for (j = 0; j < regs->size / 2; j++) { - unsigned int start = regs->regs[2 * j]; - unsigned int end = regs->regs[(2 * j) + 1]; - - if (remain < ((end - start) + 1) * 8) { - SNAPSHOT_ERR_NOMEM(device, "REGISTERS"); - goto out; - } + if (remain < ((end - start) + 1) * 8) { + SNAPSHOT_ERR_NOMEM(device, "REGISTERS"); + goto out; + } - remain -= ((end - start) + 1) * 8; + remain -= ((end - start) + 1) * 8; - for (k = start; k <= end; k++, count++) { - *data++ = k; - *data++ = *src++; - } + for (k = start; k <= end; k++, count++) { + *data++ = k; + *data++ = *src++; } } @@ -856,6 +862,7 @@ void a5xx_snapshot(struct adreno_device *adreno_dev, struct adreno_snapshot_data *snap_data = gpudev->snapshot_data; unsigned int reg, i; struct adreno_ringbuffer *rb; + struct registers regs; /* Disable Clock gating temporarily for the debug bus to work */ a5xx_hwcg_set(adreno_dev, false); @@ -873,8 +880,20 @@ void a5xx_snapshot(struct adreno_device *adreno_dev, if (device->snapshot_crashdumper) _a5xx_do_crashdump(device); - kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS, - snapshot, a5xx_snapshot_registers, NULL); + regs.regs = a5xx_registers; + regs.size = ARRAY_SIZE(a5xx_registers); + + kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS, snapshot, + a5xx_snapshot_registers, ®s); + + if (ADRENO_FEATURE(adreno_dev, ADRENO_GPMU)) { + regs.regs = a5xx_gpmu_registers; + regs.size = ARRAY_SIZE(a5xx_gpmu_registers); + + kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS, + snapshot, a5xx_snapshot_registers, ®s); + } + /* Dump SP TP HLSQ registers */ kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS, snapshot, @@ -1031,17 +1050,23 @@ void a5xx_crashdump_init(struct adreno_device *adreno_dev) * To save the registers, we need 16 bytes per register pair for the * script and a dword for each register int the data */ - for (i = 0; i < ARRAY_SIZE(_a5xx_cd_registers); i++) { - struct cdregs *regs = &_a5xx_cd_registers[i]; + /* Each pair needs 16 bytes (2 qwords) */ + script_size += (ARRAY_SIZE(a5xx_registers) / 2) * 16; + + /* Each register needs a dword in the data */ + for (j = 0; j < ARRAY_SIZE(a5xx_registers) / 2; j++) + data_size += REG_PAIR_COUNT(a5xx_registers, j) * + sizeof(unsigned int); + + if (ADRENO_FEATURE(adreno_dev, ADRENO_GPMU)) { /* Each pair needs 16 bytes (2 qwords) */ - script_size += (regs->size / 2) * 16; + script_size += (ARRAY_SIZE(a5xx_gpmu_registers) / 2) * 16; /* Each register needs a dword in the data */ - for (j = 0; j < regs->size / 2; j++) - data_size += REG_PAIR_COUNT(regs->regs, j) * + for (j = 0; j < ARRAY_SIZE(a5xx_gpmu_registers) / 2; j++) + data_size += REG_PAIR_COUNT(a5xx_gpmu_registers, j) * sizeof(unsigned int); - } /* @@ -1079,13 +1104,21 @@ void a5xx_crashdump_init(struct adreno_device *adreno_dev) ptr = (uint64_t *) capturescript.hostptr; /* For the registers, program a read command for each pair */ - for (i = 0; i < ARRAY_SIZE(_a5xx_cd_registers); i++) { - struct cdregs *regs = &_a5xx_cd_registers[i]; - for (j = 0; j < regs->size / 2; j++) { - unsigned int r = REG_PAIR_COUNT(regs->regs, j); + for (j = 0; j < ARRAY_SIZE(a5xx_registers) / 2; j++) { + unsigned int r = REG_PAIR_COUNT(a5xx_registers, j); + *ptr++ = registers.gpuaddr + offset; + *ptr++ = (((uint64_t) a5xx_registers[2 * j]) << 44) + | r; + offset += r * sizeof(unsigned int); + } + + if (ADRENO_FEATURE(adreno_dev, ADRENO_GPMU)) { + for (j = 0; j < ARRAY_SIZE(a5xx_gpmu_registers) / 2; j++) { + unsigned int r = REG_PAIR_COUNT(a5xx_gpmu_registers, j); *ptr++ = registers.gpuaddr + offset; - *ptr++ = (((uint64_t) regs->regs[2 * j]) << 44) | r; + *ptr++ = (((uint64_t) a5xx_gpmu_registers[2 * j]) << 44) + | r; offset += r * sizeof(unsigned int); } } diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c index 7604215b0306..969ca294605c 100644 --- a/drivers/gpu/msm/kgsl.c +++ b/drivers/gpu/msm/kgsl.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 @@ -2941,7 +2941,7 @@ long kgsl_ioctl_gpuobj_sync(struct kgsl_device_private *dev_priv, long ret = 0; bool full_flush = false; uint64_t size = 0; - int i, count = 0; + int i; void __user *ptr; if (param->count == 0 || param->count > 128) @@ -2953,8 +2953,8 @@ long kgsl_ioctl_gpuobj_sync(struct kgsl_device_private *dev_priv, entries = kzalloc(param->count * sizeof(*entries), GFP_KERNEL); if (entries == NULL) { - ret = -ENOMEM; - goto out; + kfree(objs); + return -ENOMEM; } ptr = to_user_ptr(param->objs); @@ -2971,36 +2971,32 @@ long kgsl_ioctl_gpuobj_sync(struct kgsl_device_private *dev_priv, if (entries[i] == NULL) continue; - count++; - if (!(objs[i].op & KGSL_GPUMEM_CACHE_RANGE)) size += entries[i]->memdesc.size; else if (objs[i].offset < entries[i]->memdesc.size) size += (entries[i]->memdesc.size - objs[i].offset); full_flush = check_full_flush(size, objs[i].op); - if (full_flush) - break; + if (full_flush) { + trace_kgsl_mem_sync_full_cache(i, size); + flush_cache_all(); + goto out; + } ptr += sizeof(*objs); } - if (full_flush) { - trace_kgsl_mem_sync_full_cache(count, size); - flush_cache_all(); - } else { - for (i = 0; !ret && i < param->count; i++) - if (entries[i]) - ret = _kgsl_gpumem_sync_cache(entries[i], - objs[i].offset, objs[i].length, - objs[i].op); - } + for (i = 0; !ret && i < param->count; i++) + if (entries[i]) + ret = _kgsl_gpumem_sync_cache(entries[i], + objs[i].offset, objs[i].length, + objs[i].op); +out: for (i = 0; i < param->count; i++) if (entries[i]) kgsl_mem_entry_put(entries[i]); -out: kfree(entries); kfree(objs); @@ -3358,7 +3354,13 @@ long kgsl_ioctl_sparse_phys_free(struct kgsl_device_private *dev_priv, if (entry == NULL) return -EINVAL; + if (!kgsl_mem_entry_set_pend(entry)) { + kgsl_mem_entry_put(entry); + return -EBUSY; + } + if (entry->memdesc.cur_bindings != 0) { + kgsl_mem_entry_unset_pend(entry); kgsl_mem_entry_put(entry); return -EINVAL; } @@ -3427,7 +3429,13 @@ long kgsl_ioctl_sparse_virt_free(struct kgsl_device_private *dev_priv, if (entry == NULL) return -EINVAL; + if (!kgsl_mem_entry_set_pend(entry)) { + kgsl_mem_entry_put(entry); + return -EBUSY; + } + if (entry->bind_tree.rb_node != NULL) { + kgsl_mem_entry_unset_pend(entry); kgsl_mem_entry_put(entry); return -EINVAL; } @@ -3967,6 +3975,7 @@ long kgsl_ioctl_gpuobj_set_info(struct kgsl_device_private *dev_priv, struct kgsl_process_private *private = dev_priv->process_priv; struct kgsl_gpuobj_set_info *param = data; struct kgsl_mem_entry *entry; + int ret = 0; if (param->id == 0) return -EINVAL; @@ -3979,12 +3988,16 @@ long kgsl_ioctl_gpuobj_set_info(struct kgsl_device_private *dev_priv, copy_metadata(entry, param->metadata, param->metadata_len); if (param->flags & KGSL_GPUOBJ_SET_INFO_TYPE) { - entry->memdesc.flags &= ~((uint64_t) KGSL_MEMTYPE_MASK); - entry->memdesc.flags |= param->type << KGSL_MEMTYPE_SHIFT; + if (param->type <= (KGSL_MEMTYPE_MASK >> KGSL_MEMTYPE_SHIFT)) { + entry->memdesc.flags &= ~((uint64_t) KGSL_MEMTYPE_MASK); + entry->memdesc.flags |= (uint64_t)((param->type << + KGSL_MEMTYPE_SHIFT) & KGSL_MEMTYPE_MASK); + } else + ret = -EINVAL; } kgsl_mem_entry_put(entry); - return 0; + return ret; } long kgsl_ioctl_cff_syncmem(struct kgsl_device_private *dev_priv, diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c index 57d99c451952..91465e4e251b 100644 --- a/drivers/gpu/msm/kgsl_iommu.c +++ b/drivers/gpu/msm/kgsl_iommu.c @@ -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 @@ -38,9 +38,10 @@ #define _IOMMU_PRIV(_mmu) (&((_mmu)->priv.iommu)) -#define ADDR_IN_GLOBAL(_a) \ - (((_a) >= KGSL_IOMMU_GLOBAL_MEM_BASE) && \ - ((_a) < (KGSL_IOMMU_GLOBAL_MEM_BASE + KGSL_IOMMU_GLOBAL_MEM_SIZE))) +#define ADDR_IN_GLOBAL(_mmu, _a) \ + (((_a) >= KGSL_IOMMU_GLOBAL_MEM_BASE(_mmu)) && \ + ((_a) < (KGSL_IOMMU_GLOBAL_MEM_BASE(_mmu) + \ + KGSL_IOMMU_GLOBAL_MEM_SIZE))) static struct kgsl_mmu_pt_ops iommu_pt_ops; static bool need_iommu_sync; @@ -200,7 +201,7 @@ static void kgsl_iommu_add_global(struct kgsl_mmu *mmu, BUG_ON(global_pt_count >= GLOBAL_PT_ENTRIES); BUG_ON((global_pt_alloc + memdesc->size) >= KGSL_IOMMU_GLOBAL_MEM_SIZE); - memdesc->gpuaddr = KGSL_IOMMU_GLOBAL_MEM_BASE + global_pt_alloc; + memdesc->gpuaddr = KGSL_IOMMU_GLOBAL_MEM_BASE(mmu) + global_pt_alloc; memdesc->priv |= KGSL_MEMDESC_GLOBAL; global_pt_alloc += memdesc->size; @@ -213,7 +214,7 @@ static void kgsl_iommu_add_global(struct kgsl_mmu *mmu, void kgsl_add_global_secure_entry(struct kgsl_device *device, struct kgsl_memdesc *memdesc) { - memdesc->gpuaddr = KGSL_IOMMU_SECURE_BASE; + memdesc->gpuaddr = KGSL_IOMMU_SECURE_BASE(&device->mmu); kgsl_global_secure_pt_entry = memdesc; } @@ -686,7 +687,7 @@ static void _find_mem_entries(struct kgsl_mmu *mmu, uint64_t faultaddr, /* Set the maximum possible size as an initial value */ nextentry->gpuaddr = (uint64_t) -1; - if (ADDR_IN_GLOBAL(faultaddr)) { + if (ADDR_IN_GLOBAL(mmu, faultaddr)) { _get_global_entries(faultaddr, preventry, nextentry); } else if (context) { private = context->proc_priv; @@ -1056,14 +1057,14 @@ static void setup_64bit_pagetable(struct kgsl_mmu *mmu, unsigned int secure_global_size = kgsl_global_secure_pt_entry != NULL ? kgsl_global_secure_pt_entry->size : 0; if (mmu->secured && pagetable->name == KGSL_MMU_SECURE_PT) { - pt->compat_va_start = KGSL_IOMMU_SECURE_BASE + + pt->compat_va_start = KGSL_IOMMU_SECURE_BASE(mmu) + secure_global_size; - pt->compat_va_end = KGSL_IOMMU_SECURE_END; - pt->va_start = KGSL_IOMMU_SECURE_BASE + secure_global_size; - pt->va_end = KGSL_IOMMU_SECURE_END; + pt->compat_va_end = KGSL_IOMMU_SECURE_END(mmu); + pt->va_start = KGSL_IOMMU_SECURE_BASE(mmu) + secure_global_size; + pt->va_end = KGSL_IOMMU_SECURE_END(mmu); } else { pt->compat_va_start = KGSL_IOMMU_SVM_BASE32; - pt->compat_va_end = KGSL_IOMMU_SVM_END32; + pt->compat_va_end = KGSL_IOMMU_SECURE_BASE(mmu); pt->va_start = KGSL_IOMMU_VA_BASE64; pt->va_end = KGSL_IOMMU_VA_END64; } @@ -1072,7 +1073,7 @@ static void setup_64bit_pagetable(struct kgsl_mmu *mmu, pagetable->name != KGSL_MMU_SECURE_PT) { if ((BITS_PER_LONG == 32) || is_compat_task()) { pt->svm_start = KGSL_IOMMU_SVM_BASE32; - pt->svm_end = KGSL_IOMMU_SVM_END32; + pt->svm_end = KGSL_IOMMU_SECURE_BASE(mmu); } else { pt->svm_start = KGSL_IOMMU_SVM_BASE64; pt->svm_end = KGSL_IOMMU_SVM_END64; @@ -1088,22 +1089,22 @@ static void setup_32bit_pagetable(struct kgsl_mmu *mmu, kgsl_global_secure_pt_entry->size : 0; if (mmu->secured) { if (pagetable->name == KGSL_MMU_SECURE_PT) { - pt->compat_va_start = KGSL_IOMMU_SECURE_BASE + + pt->compat_va_start = KGSL_IOMMU_SECURE_BASE(mmu) + secure_global_size; - pt->compat_va_end = KGSL_IOMMU_SECURE_END; - pt->va_start = KGSL_IOMMU_SECURE_BASE + + pt->compat_va_end = KGSL_IOMMU_SECURE_END(mmu); + pt->va_start = KGSL_IOMMU_SECURE_BASE(mmu) + secure_global_size; - pt->va_end = KGSL_IOMMU_SECURE_END; + pt->va_end = KGSL_IOMMU_SECURE_END(mmu); } else { pt->va_start = KGSL_IOMMU_SVM_BASE32; - pt->va_end = KGSL_IOMMU_SECURE_BASE + + pt->va_end = KGSL_IOMMU_SECURE_BASE(mmu) + secure_global_size; pt->compat_va_start = pt->va_start; pt->compat_va_end = pt->va_end; } } else { pt->va_start = KGSL_IOMMU_SVM_BASE32; - pt->va_end = KGSL_IOMMU_GLOBAL_MEM_BASE; + pt->va_end = KGSL_IOMMU_GLOBAL_MEM_BASE(mmu); pt->compat_va_start = pt->va_start; pt->compat_va_end = pt->va_end; } @@ -2354,7 +2355,8 @@ static int kgsl_iommu_set_svm_region(struct kgsl_pagetable *pagetable, struct rb_node *node; /* Make sure the requested address doesn't fall in the global range */ - if (ADDR_IN_GLOBAL(gpuaddr) || ADDR_IN_GLOBAL(gpuaddr + size)) + if (ADDR_IN_GLOBAL(pagetable->mmu, gpuaddr) || + ADDR_IN_GLOBAL(pagetable->mmu, gpuaddr + size)) return -ENOMEM; spin_lock(&pagetable->lock); diff --git a/drivers/gpu/msm/kgsl_iommu.h b/drivers/gpu/msm/kgsl_iommu.h index 06f6d65effad..a21e74f92d7c 100644 --- a/drivers/gpu/msm/kgsl_iommu.h +++ b/drivers/gpu/msm/kgsl_iommu.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-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 @@ -24,12 +24,17 @@ * are mapped into all pagetables. */ #define KGSL_IOMMU_GLOBAL_MEM_SIZE SZ_8M -#define KGSL_IOMMU_GLOBAL_MEM_BASE 0xf8000000 +#define KGSL_IOMMU_GLOBAL_MEM_BASE32 0xf8000000 +#define KGSL_IOMMU_GLOBAL_MEM_BASE64 0xfc000000 + +#define KGSL_IOMMU_GLOBAL_MEM_BASE(__mmu) \ + (MMU_FEATURE(__mmu, KGSL_MMU_64BIT) ? \ + KGSL_IOMMU_GLOBAL_MEM_BASE64 : KGSL_IOMMU_GLOBAL_MEM_BASE32) #define KGSL_IOMMU_SECURE_SIZE SZ_256M -#define KGSL_IOMMU_SECURE_END KGSL_IOMMU_GLOBAL_MEM_BASE -#define KGSL_IOMMU_SECURE_BASE \ - (KGSL_IOMMU_GLOBAL_MEM_BASE - KGSL_IOMMU_SECURE_SIZE) +#define KGSL_IOMMU_SECURE_END(_mmu) KGSL_IOMMU_GLOBAL_MEM_BASE(_mmu) +#define KGSL_IOMMU_SECURE_BASE(_mmu) \ + (KGSL_IOMMU_GLOBAL_MEM_BASE(_mmu) - KGSL_IOMMU_SECURE_SIZE) #define KGSL_IOMMU_SVM_BASE32 0x300000 #define KGSL_IOMMU_SVM_END32 (0xC0000000 - SZ_16M) diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 0df32fe0e345..b0eeb5090c91 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -971,6 +971,8 @@ static int usbhid_parse(struct hid_device *hid) unsigned int rsize = 0; char *rdesc; int ret, n; + int num_descriptors; + size_t offset = offsetof(struct hid_descriptor, desc); quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); @@ -993,10 +995,18 @@ static int usbhid_parse(struct hid_device *hid) return -ENODEV; } + if (hdesc->bLength < sizeof(struct hid_descriptor)) { + dbg_hid("hid descriptor is too short\n"); + return -EINVAL; + } + hid->version = le16_to_cpu(hdesc->bcdHID); hid->country = hdesc->bCountryCode; - for (n = 0; n < hdesc->bNumDescriptors; n++) + num_descriptors = min_t(int, hdesc->bNumDescriptors, + (hdesc->bLength - offset) / sizeof(struct hid_class_descriptor)); + + for (n = 0; n < num_descriptors; n++) if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT) rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength); diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 700145b15088..da8fd9580223 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -510,13 +510,13 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd, goto inval; field = report->field[uref->field_index]; + } - if (cmd == HIDIOCGCOLLECTIONINDEX) { - if (uref->usage_index >= field->maxusage) - goto inval; - } else if (uref->usage_index >= field->report_count) + if (cmd == HIDIOCGCOLLECTIONINDEX) { + if (uref->usage_index >= field->maxusage) goto inval; - } + } else if (uref->usage_index >= field->report_count) + goto inval; if ((cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) && (uref_multi->num_values > HID_MAX_MULTI_USAGES || diff --git a/drivers/input/tablet/gtco.c b/drivers/input/tablet/gtco.c index 7c18249d6c8e..8b68a210277b 100644 --- a/drivers/input/tablet/gtco.c +++ b/drivers/input/tablet/gtco.c @@ -231,13 +231,17 @@ static void parse_hid_report_descriptor(struct gtco *device, char * report, /* Walk this report and pull out the info we need */ while (i < length) { - prefix = report[i]; - - /* Skip over prefix */ - i++; + prefix = report[i++]; /* Determine data size and save the data in the proper variable */ - size = PREF_SIZE(prefix); + size = (1U << PREF_SIZE(prefix)) >> 1; + if (i + size > length) { + dev_err(ddev, + "Not enough data (need %d, have %d)\n", + i + size, length); + break; + } + switch (size) { case 1: data = report[i]; @@ -245,8 +249,7 @@ static void parse_hid_report_descriptor(struct gtco *device, char * report, case 2: data16 = get_unaligned_le16(&report[i]); break; - case 3: - size = 4; + case 4: data32 = get_unaligned_le32(&report[i]); break; } diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c index 4cba01c56d47..ad65039a03b5 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. * Copyright (C) 2018 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify @@ -402,7 +402,7 @@ led_brightness qpnp_flash_led_brightness_get(struct led_classdev *led_cdev) static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) { int rc, i, addr_offset; - u8 val = 0, mask; + u8 val = 0, mask, strobe_mask = 0, strobe_ctrl; for (i = 0; i < led->num_fnodes; i++) { addr_offset = led->fnode[i].id; @@ -413,6 +413,51 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) return rc; val |= 0x1 << led->fnode[i].id; + + if (led->fnode[i].strobe_sel == HW_STROBE) { + if (led->fnode[i].id == LED3) + strobe_mask |= LED3_FLASH_ONCE_ONLY_BIT; + else + strobe_mask |= LED1N2_FLASH_ONCE_ONLY_BIT; + } + + if (led->fnode[i].id == LED3 && + led->fnode[i].strobe_sel == LPG_STROBE) + strobe_mask |= LED3_FLASH_ONCE_ONLY_BIT; + /* + * As per the hardware recommendation, to use LED2/LED3 in HW + * strobe mode, LED1 should be set to HW strobe mode as well. + */ + if (led->fnode[i].strobe_sel == HW_STROBE && + (led->fnode[i].id == LED2 || led->fnode[i].id == LED3)) { + mask = FLASH_HW_STROBE_MASK; + addr_offset = led->fnode[LED1].id; + /* + * HW_STROBE: enable, TRIGGER: level, + * POLARITY: active high + */ + strobe_ctrl = BIT(2) | BIT(0); + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_STROBE_CTRL( + led->base + addr_offset), + mask, strobe_ctrl); + if (rc < 0) + return rc; + } + } + + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_MULTI_STROBE_CTRL(led->base), + strobe_mask, 0); + if (rc < 0) + return rc; + + if (led->fnode[LED3].strobe_sel == LPG_STROBE) { + rc = qpnp_flash_led_masked_write(led, + FLASH_LED_REG_LPG_INPUT_CTRL(led->base), + LPG_INPUT_SEL_BIT, LPG_INPUT_SEL_BIT); + if (rc < 0) + return rc; } rc = qpnp_flash_led_write(led, @@ -606,19 +651,6 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) return rc; } - if (led->fnode[LED3].strobe_sel == LPG_STROBE) { - rc = qpnp_flash_led_masked_write(led, - FLASH_LED_REG_MULTI_STROBE_CTRL(led->base), - LED3_FLASH_ONCE_ONLY_BIT, 0); - if (rc < 0) - return rc; - - rc = qpnp_flash_led_masked_write(led, - FLASH_LED_REG_LPG_INPUT_CTRL(led->base), - LPG_INPUT_SEL_BIT, LPG_INPUT_SEL_BIT); - if (rc < 0) - return rc; - } return 0; } diff --git a/drivers/media/platform/msm/ais/camera/camera.c b/drivers/media/platform/msm/ais/camera/camera.c index 3f477d50ceaf..afba7386a82b 100644 --- a/drivers/media/platform/msm/ais/camera/camera.c +++ b/drivers/media/platform/msm/ais/camera/camera.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 @@ -464,7 +464,9 @@ static int camera_v4l2_subscribe_event(struct v4l2_fh *fh, int rc = 0; struct camera_v4l2_private *sp = fh_to_private(fh); + mutex_lock(&sp->lock); rc = v4l2_event_subscribe(&sp->fh, sub, 5, NULL); + mutex_unlock(&sp->lock); return rc; } @@ -475,7 +477,9 @@ static int camera_v4l2_unsubscribe_event(struct v4l2_fh *fh, int rc = 0; struct camera_v4l2_private *sp = fh_to_private(fh); + mutex_lock(&sp->lock); rc = v4l2_event_unsubscribe(&sp->fh, sub); + mutex_unlock(&sp->lock); return rc; } diff --git a/drivers/media/platform/msm/ais/isp/msm_isp47.c b/drivers/media/platform/msm/ais/isp/msm_isp47.c index 04e879fc3bcf..d8cc1b75d0b5 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp47.c +++ b/drivers/media/platform/msm/ais/isp/msm_isp47.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 @@ -1032,16 +1032,18 @@ int msm_vfe47_start_fetch_engine(struct vfe_device *vfe_dev, vfe_dev->buf_mgr, fe_cfg->session_id, fe_cfg->stream_id); vfe_dev->fetch_engine_info.bufq_handle = bufq_handle; - + mutex_lock(&vfe_dev->buf_mgr->lock); rc = vfe_dev->buf_mgr->ops->get_buf_by_index( vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf); if (rc < 0 || !buf) { pr_err("%s: No fetch buffer rc= %d buf= %pK\n", __func__, rc, buf); + mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } mapped_info = buf->mapped_info[0]; buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED; + mutex_unlock(&vfe_dev->buf_mgr->lock); } else { rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr, &mapped_info, fe_cfg->fd); @@ -1091,15 +1093,18 @@ int msm_vfe47_start_fetch_engine_multi_pass(struct vfe_device *vfe_dev, fe_cfg->stream_id); vfe_dev->fetch_engine_info.bufq_handle = bufq_handle; + mutex_lock(&vfe_dev->buf_mgr->lock); rc = vfe_dev->buf_mgr->ops->get_buf_by_index( vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf); if (rc < 0 || !buf) { pr_err("%s: No fetch buffer rc= %d buf= %pK\n", __func__, rc, buf); + mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } mapped_info = buf->mapped_info[0]; buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED; + mutex_unlock(&vfe_dev->buf_mgr->lock); } else { rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr, &mapped_info, fe_cfg->fd); diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c index a85ee30769c4..b05ae9936da0 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/ais/isp/msm_isp_axi_util.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 @@ -2808,9 +2808,11 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev, vfe_dev->axi_data.src_info[VFE_PIX_0].eof_id = 0; } + mutex_lock(&vfe_dev->buf_mgr->lock); for (i = 0; i < stream_cfg_cmd->num_streams; i++) { if (HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i]) >= VFE_AXI_SRC_MAX) { + mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } stream_info = &axi_data->stream_info[ @@ -2820,6 +2822,7 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev, SRC_TO_INTF(stream_info->stream_src)].active; else { ISP_DBG("%s: invalid src info index\n", __func__); + mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } @@ -2834,6 +2837,7 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev, HANDLE_TO_IDX( stream_cfg_cmd->stream_handle[i])); spin_unlock_irqrestore(&stream_info->lock, flags); + mutex_unlock(&vfe_dev->buf_mgr->lock); return rc; } @@ -2892,6 +2896,7 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev, } } + mutex_unlock(&vfe_dev->buf_mgr->lock); msm_isp_update_stream_bandwidth(vfe_dev, stream_cfg_cmd->hw_state); vfe_dev->hw_info->vfe_ops.axi_ops.reload_wm(vfe_dev, vfe_dev->vfe_base, wm_reload_mask); @@ -3821,10 +3826,12 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) &update_cmd->update_info[i]; stream_info = &axi_data->stream_info[HANDLE_TO_IDX( update_info->stream_handle)]; + mutex_lock(&vfe_dev->buf_mgr->lock); rc = msm_isp_request_frame(vfe_dev, stream_info, update_info->user_stream_id, update_info->frame_id, MSM_ISP_INVALID_BUF_INDEX); + mutex_unlock(&vfe_dev->buf_mgr->lock); if (rc) pr_err("%s failed to request frame!\n", __func__); @@ -3897,10 +3904,12 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) stream_info = &axi_data->stream_info[HANDLE_TO_IDX( req_frm->stream_handle)]; + mutex_lock(&vfe_dev->buf_mgr->lock); rc = msm_isp_request_frame(vfe_dev, stream_info, req_frm->user_stream_id, req_frm->frame_id, req_frm->buf_index); + mutex_unlock(&vfe_dev->buf_mgr->lock); if (rc) pr_err("%s failed to request frame!\n", __func__); diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c index 0d08cffda25c..360eb8eca8d7 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.c +++ b/drivers/media/platform/msm/ais/isp/msm_isp_stats_util.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 @@ -684,18 +684,23 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev, stream_cfg_cmd->num_streams); return -EINVAL; } + mutex_lock(&vfe_dev->buf_mgr->lock); + num_stats_comp_mask = vfe_dev->hw_info->stats_hw_info->num_stats_comp_mask; rc = vfe_dev->hw_info->vfe_ops.stats_ops.check_streams( stats_data->stream_info); - if (rc < 0) + if (rc < 0) { + mutex_unlock(&vfe_dev->buf_mgr->lock); return rc; + } for (i = 0; i < stream_cfg_cmd->num_streams; i++) { idx = STATS_IDX(stream_cfg_cmd->stream_handle[i]); if (idx >= vfe_dev->hw_info->stats_hw_info->num_stats_type) { pr_err("%s Invalid stats index %d", __func__, idx); + mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } @@ -711,11 +716,13 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev, pr_err("%s: comp grp %d exceed max %d\n", __func__, stream_info->composite_flag, num_stats_comp_mask); + mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } rc = msm_isp_init_stats_ping_pong_reg(vfe_dev, stream_info); if (rc < 0) { pr_err("%s: No buffer for stream%d\n", __func__, idx); + mutex_unlock(&vfe_dev->buf_mgr->lock); return rc; } if (!stream_info->composite_flag) @@ -740,6 +747,7 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev, stats_data->num_active_stream); } + mutex_unlock(&vfe_dev->buf_mgr->lock); if (vfe_dev->axi_data.src_info[VFE_PIX_0].active) { rc = msm_isp_stats_wait_for_cfg_done(vfe_dev); diff --git a/drivers/media/platform/msm/ais/isp/msm_isp_util.c b/drivers/media/platform/msm/ais/isp/msm_isp_util.c index 9e5317eb2920..e2ed2d3eb1bc 100644 --- a/drivers/media/platform/msm/ais/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/ais/isp/msm_isp_util.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 @@ -392,9 +392,10 @@ static int msm_isp_start_fetch_engine_multi_pass(struct vfe_device *vfe_dev, vfe_dev->hw_info->vfe_ops.core_ops.reset_hw(vfe_dev, 0, 1); msm_isp_reset_framedrop(vfe_dev, stream_info); - + mutex_lock(&vfe_dev->buf_mgr->lock); rc = msm_isp_cfg_offline_ping_pong_address(vfe_dev, stream_info, VFE_PING_FLAG, fe_cfg->output_buf_idx); + mutex_unlock(&vfe_dev->buf_mgr->lock); if (rc < 0) { pr_err("%s: Fetch engine config failed\n", __func__); return -EINVAL; @@ -947,6 +948,7 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd, break; case VIDIOC_MSM_ISP_AXI_RESTART: mutex_lock(&vfe_dev->core_mutex); + mutex_lock(&vfe_dev->buf_mgr->lock); if (atomic_read(&vfe_dev->error_info.overflow_state) != HALT_ENFORCED) { rc = msm_isp_stats_restart(vfe_dev); @@ -957,6 +959,7 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd, pr_err_ratelimited("%s: no AXI restart, halt enforced.\n", __func__); } + mutex_unlock(&vfe_dev->buf_mgr->lock); mutex_unlock(&vfe_dev->core_mutex); break; case VIDIOC_MSM_ISP_INPUT_CFG: diff --git a/drivers/media/platform/msm/ais/jpeg_10/msm_jpeg_dev.c b/drivers/media/platform/msm/ais/jpeg_10/msm_jpeg_dev.c index 88c822c50491..2e3b4fe6b025 100644 --- a/drivers/media/platform/msm/ais/jpeg_10/msm_jpeg_dev.c +++ b/drivers/media/platform/msm/ais/jpeg_10/msm_jpeg_dev.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 @@ -32,6 +32,8 @@ #define MSM_JPEG_NAME "jpeg" #define DEV_NAME_LEN 10 +static char devname[DEV_NAME_LEN]; + static int msm_jpeg_open(struct inode *inode, struct file *filp) { int rc = 0; @@ -185,7 +187,6 @@ static int msm_jpeg_init_dev(struct platform_device *pdev) struct msm_jpeg_device *msm_jpeg_device_p; const struct of_device_id *device_id; const struct msm_jpeg_priv_data *priv_data; - char devname[DEV_NAME_LEN]; msm_jpeg_device_p = kzalloc(sizeof(struct msm_jpeg_device), GFP_ATOMIC); if (!msm_jpeg_device_p) { diff --git a/drivers/media/platform/msm/ais/jpeg_dma/msm_jpeg_dma_dev.c b/drivers/media/platform/msm/ais/jpeg_dma/msm_jpeg_dma_dev.c index 76fe7dfa68cb..61200d379a1d 100644 --- a/drivers/media/platform/msm/ais/jpeg_dma/msm_jpeg_dma_dev.c +++ b/drivers/media/platform/msm/ais/jpeg_dma/msm_jpeg_dma_dev.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 @@ -754,9 +754,12 @@ static int msm_jpegdma_s_fmt_vid_out(struct file *file, static int msm_jpegdma_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *req) { + int ret = 0; struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh); - - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, req); + mutex_lock(&ctx->lock); + ret = v4l2_m2m_reqbufs(file, ctx->m2m_ctx, req); + mutex_unlock(&ctx->lock); + return ret; } /* @@ -833,11 +836,11 @@ static int msm_jpegdma_streamoff(struct file *file, { struct jpegdma_ctx *ctx = msm_jpegdma_ctx_from_fh(fh); int ret; - + mutex_lock(&ctx->lock); ret = v4l2_m2m_streamoff(file, ctx->m2m_ctx, buf_type); if (ret < 0) dev_err(ctx->jdma_device->dev, "Stream off fails\n"); - + mutex_unlock(&ctx->lock); return ret; } diff --git a/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c index 260c1dc3e963..4a23fe0e9c7e 100644 --- a/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/ais/sensor/actuator/msm_actuator.c @@ -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 @@ -646,6 +646,8 @@ static int32_t msm_actuator_move_focus( a_ctrl->curr_step_pos, dest_step_pos, curr_lens_pos); while (a_ctrl->curr_step_pos != dest_step_pos) { + if (a_ctrl->curr_region_index >= a_ctrl->region_size) + break; step_boundary = a_ctrl->region_params[a_ctrl->curr_region_index]. step_bound[dir]; diff --git a/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c b/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c index 79231fb314ad..ae3f7e89a8b4 100644 --- a/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.c +++ b/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.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 @@ -25,8 +25,8 @@ #define MSM_CAMERA_TZ_BOOT_PROTECTED (false) /* Update version major number in case the HLOS-TA interface is changed*/ -#define TA_IF_VERSION_MAJ 1 -#define TA_IF_VERSION_MIN 2 +#define TA_IF_VERSION_MAJ 2 +#define TA_IF_VERSION_MIN 1 #undef CDBG #ifdef MSM_CAMERA_TZ_UTIL_VERBOSE diff --git a/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.h b/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.h index 146b7d029984..eaa77db3957c 100644 --- a/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.h +++ b/drivers/media/platform/msm/camera_v2/common/msm_camera_tz_util.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, 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 @@ -29,7 +29,7 @@ #define MSM_CAMERA_TZ_HW_BLOCK_CPP 0x0000000010 enum msm_camera_tz_cmd_id_t { - MSM_CAMERA_TZ_CMD_NONE, + MSM_CAMERA_TZ_CMD_NONE = 56000, MSM_CAMERA_TZ_CMD_GET_IF_VERSION, MSM_CAMERA_TZ_CMD_POWER_UP, MSM_CAMERA_TZ_CMD_POWER_DOWN, diff --git a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c index d881b4aea48f..8e0a7443f98c 100644 --- a/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.c +++ b/drivers/media/platform/msm/camera_v2/fd/msm_fd_dev.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 @@ -442,7 +442,7 @@ static int msm_fd_open(struct file *file) } ctx->mem_pool.fd_device = ctx->fd_device; - ctx->stats = vmalloc(sizeof(*ctx->stats) * MSM_FD_MAX_RESULT_BUFS); + ctx->stats = vzalloc(sizeof(*ctx->stats) * MSM_FD_MAX_RESULT_BUFS); if (!ctx->stats) { dev_err(device->dev, "No memory for face statistics\n"); ret = -ENOMEM; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h index d336e1ef1bd7..acf0a90ed93d 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp.h @@ -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 @@ -155,9 +155,11 @@ struct msm_vfe_irq_ops { struct msm_isp_timestamp *ts); void (*process_axi_irq)(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, + uint32_t pingpong_status, struct msm_isp_timestamp *ts); void (*process_stats_irq)(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, + uint32_t pingpong_status, struct msm_isp_timestamp *ts); void (*config_irq)(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, @@ -596,6 +598,7 @@ struct msm_vfe_tasklet_queue_cmd { struct list_head list; uint32_t vfeInterruptStatus0; uint32_t vfeInterruptStatus1; + uint32_t vfe_pingpong_status; struct msm_isp_timestamp ts; uint8_t cmd_used; struct vfe_device *vfe_dev; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c index 52269339d8bb..2eab3dd5f812 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.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 @@ -1051,15 +1051,18 @@ static int msm_vfe40_start_fetch_engine(struct vfe_device *vfe_dev, fe_cfg->stream_id); vfe_dev->fetch_engine_info.bufq_handle = bufq_handle; + mutex_lock(&vfe_dev->buf_mgr->lock); rc = vfe_dev->buf_mgr->ops->get_buf_by_index( vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf); if (rc < 0 || !buf) { pr_err("%s: No fetch buffer rc= %d buf= %pK\n", __func__, rc, buf); + mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } mapped_info = buf->mapped_info[0]; buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED; + mutex_unlock(&vfe_dev->buf_mgr->lock); } else { rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr, &mapped_info, fe_cfg->fd); @@ -1109,15 +1112,18 @@ static int msm_vfe40_start_fetch_engine_multi_pass(struct vfe_device *vfe_dev, fe_cfg->stream_id); vfe_dev->fetch_engine_info.bufq_handle = bufq_handle; + mutex_lock(&vfe_dev->buf_mgr->lock); rc = vfe_dev->buf_mgr->ops->get_buf_by_index( vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf); if (rc < 0 || !buf) { pr_err("%s: No fetch buffer rc= %d buf= %pK\n", __func__, rc, buf); + mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } mapped_info = buf->mapped_info[0]; buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED; + mutex_unlock(&vfe_dev->buf_mgr->lock); } else { rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr, &mapped_info, fe_cfg->fd); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c index cc4dd5eaf93e..3b8de1a13c88 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.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 @@ -891,14 +891,18 @@ static int msm_vfe44_fetch_engine_start(struct vfe_device *vfe_dev, vfe_dev->buf_mgr, fe_cfg->session_id, fe_cfg->stream_id); vfe_dev->fetch_engine_info.bufq_handle = bufq_handle; + + mutex_lock(&vfe_dev->buf_mgr->lock); rc = vfe_dev->buf_mgr->ops->get_buf_by_index( vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf); if (rc < 0) { pr_err("%s: No fetch buffer\n", __func__); + mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } mapped_info = buf->mapped_info[0]; buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED; + mutex_unlock(&vfe_dev->buf_mgr->lock); } else { rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr, &mapped_info, fe_cfg->fd); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c index 632624034a04..f8866b01e617 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.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 @@ -833,15 +833,18 @@ static int msm_vfe46_start_fetch_engine(struct vfe_device *vfe_dev, fe_cfg->stream_id); vfe_dev->fetch_engine_info.bufq_handle = bufq_handle; + mutex_lock(&vfe_dev->buf_mgr->lock); rc = vfe_dev->buf_mgr->ops->get_buf_by_index( vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf); if (rc < 0 || !buf) { pr_err("%s: No fetch buffer rc= %d buf= %pK\n", __func__, rc, buf); + mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } mapped_info = buf->mapped_info[0]; buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED; + mutex_unlock(&vfe_dev->buf_mgr->lock); } else { rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr, &mapped_info, fe_cfg->fd); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c index 360121e89260..0e091f67681f 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.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 @@ -570,6 +570,7 @@ void msm_vfe47_process_error_status(struct vfe_device *vfe_dev) void msm_vfe47_read_and_clear_irq_status(struct vfe_device *vfe_dev, uint32_t *irq_status0, uint32_t *irq_status1) { + uint32_t count = 0; *irq_status0 = msm_camera_io_r(vfe_dev->vfe_base + 0x6C); *irq_status1 = msm_camera_io_r(vfe_dev->vfe_base + 0x70); /* Mask off bits that are not enabled */ @@ -578,6 +579,14 @@ void msm_vfe47_read_and_clear_irq_status(struct vfe_device *vfe_dev, msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x58); *irq_status0 &= vfe_dev->irq0_mask; *irq_status1 &= vfe_dev->irq1_mask; + /* check if status register is cleared if not clear again*/ + while (*irq_status0 && + (*irq_status0 & msm_camera_io_r(vfe_dev->vfe_base + 0x6C)) && + (count < MAX_RECOVERY_THRESHOLD)) { + msm_camera_io_w(*irq_status0, vfe_dev->vfe_base + 0x64); + msm_camera_io_w_mb(1, vfe_dev->vfe_base + 0x58); + count++; + } if (*irq_status1 & (1 << 0)) { vfe_dev->error_info.camif_status = @@ -1095,15 +1104,18 @@ int msm_vfe47_start_fetch_engine(struct vfe_device *vfe_dev, fe_cfg->stream_id); vfe_dev->fetch_engine_info.bufq_handle = bufq_handle; + mutex_lock(&vfe_dev->buf_mgr->lock); rc = vfe_dev->buf_mgr->ops->get_buf_by_index( vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf); if (rc < 0 || !buf) { pr_err("%s: No fetch buffer rc= %d buf= %pK\n", __func__, rc, buf); + mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } mapped_info = buf->mapped_info[0]; buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED; + mutex_unlock(&vfe_dev->buf_mgr->lock); } else { rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr, &mapped_info, fe_cfg->fd); @@ -1153,15 +1165,18 @@ int msm_vfe47_start_fetch_engine_multi_pass(struct vfe_device *vfe_dev, fe_cfg->stream_id); vfe_dev->fetch_engine_info.bufq_handle = bufq_handle; + mutex_lock(&vfe_dev->buf_mgr->lock); rc = vfe_dev->buf_mgr->ops->get_buf_by_index( vfe_dev->buf_mgr, bufq_handle, fe_cfg->buf_idx, &buf); if (rc < 0 || !buf) { pr_err("%s: No fetch buffer rc= %d buf= %pK\n", __func__, rc, buf); + mutex_unlock(&vfe_dev->buf_mgr->lock); return -EINVAL; } mapped_info = buf->mapped_info[0]; buf->state = MSM_ISP_BUFFER_STATE_DISPATCHED; + mutex_unlock(&vfe_dev->buf_mgr->lock); } else { rc = vfe_dev->buf_mgr->ops->map_buf(vfe_dev->buf_mgr, &mapped_info, fe_cfg->fd); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index f9a4c27fba83..54f8cc00c334 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.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. * Copyright (C) 2018 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify @@ -3105,12 +3105,18 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, return -EINVAL; msm_isp_get_timestamp(×tamp, vfe_dev_ioctl); - + mutex_lock(&vfe_dev_ioctl->buf_mgr->lock); for (i = 0; i < stream_cfg_cmd->num_streams; i++) { if (stream_cfg_cmd->stream_handle[i] == 0) continue; stream_info = msm_isp_get_stream_common_data(vfe_dev_ioctl, HANDLE_TO_IDX(stream_cfg_cmd->stream_handle[i])); + + if (!stream_info) { + pr_err("%s: stream_info is NULL", __func__); + mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); + return -EINVAL; + } if (SRC_TO_INTF(stream_info->stream_src) < VFE_SRC_MAX) src_state = axi_data->src_info[ SRC_TO_INTF(stream_info->stream_src)].active; @@ -3118,6 +3124,7 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, else { ISP_DBG("%s: invalid src info index\n", __func__); rc = -EINVAL; + mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); goto error; } spin_lock_irqsave(&stream_info->lock, flags); @@ -3129,6 +3136,7 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, } if (rc) { spin_unlock_irqrestore(&stream_info->lock, flags); + mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); goto error; } @@ -3151,6 +3159,7 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, HANDLE_TO_IDX( stream_cfg_cmd->stream_handle[i])); spin_unlock_irqrestore(&stream_info->lock, flags); + mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); goto error; } for (k = 0; k < stream_info->num_isp; k++) { @@ -3209,6 +3218,7 @@ static int msm_isp_start_axi_stream(struct vfe_device *vfe_dev_ioctl, spin_unlock_irqrestore(&stream_info->lock, flags); streams[num_streams++] = stream_info; } + mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); for (i = 0; i < MAX_VFE; i++) { vfe_dev = update_vfes[i]; @@ -3961,10 +3971,12 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) &update_cmd->update_info[i]; stream_info = msm_isp_get_stream_common_data(vfe_dev, HANDLE_TO_IDX(update_info->stream_handle)); + mutex_lock(&vfe_dev->buf_mgr->lock); rc = msm_isp_request_frame(vfe_dev, stream_info, update_info->user_stream_id, update_info->frame_id, MSM_ISP_INVALID_BUF_INDEX); + mutex_unlock(&vfe_dev->buf_mgr->lock); if (rc) pr_err("%s failed to request frame!\n", __func__); @@ -4010,10 +4022,12 @@ int msm_isp_update_axi_stream(struct vfe_device *vfe_dev, void *arg) rc = -EINVAL; break; } + mutex_lock(&vfe_dev->buf_mgr->lock); rc = msm_isp_request_frame(vfe_dev, stream_info, req_frm->user_stream_id, req_frm->frame_id, req_frm->buf_index); + mutex_unlock(&vfe_dev->buf_mgr->lock); if (rc) pr_err("%s failed to request frame!\n", __func__); @@ -4212,11 +4226,11 @@ void msm_isp_process_axi_irq_stream(struct vfe_device *vfe_dev, void msm_isp_process_axi_irq(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, - struct msm_isp_timestamp *ts) + uint32_t pingpong_status, struct msm_isp_timestamp *ts) { int i, rc = 0; uint32_t comp_mask = 0, wm_mask = 0; - uint32_t pingpong_status, stream_idx; + uint32_t stream_idx; struct msm_vfe_axi_stream *stream_info; struct msm_vfe_axi_composite_info *comp_info; struct msm_vfe_axi_shared_data *axi_data = &vfe_dev->axi_data; @@ -4230,8 +4244,6 @@ void msm_isp_process_axi_irq(struct vfe_device *vfe_dev, return; ISP_DBG("%s: status: 0x%x\n", __func__, irq_status0); - pingpong_status = - vfe_dev->hw_info->vfe_ops.axi_ops.get_pingpong_status(vfe_dev); for (i = 0; i < axi_data->hw_info->num_comp_mask; i++) { rc = 0; diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h index 0f029c0d5178..9794db5a1b9c 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.h @@ -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 @@ -54,7 +54,7 @@ void msm_isp_notify(struct vfe_device *vfe_dev, uint32_t event_type, void msm_isp_process_axi_irq(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, - struct msm_isp_timestamp *ts); + uint32_t pingpong_status, struct msm_isp_timestamp *ts); void msm_isp_axi_disable_all_wm(struct vfe_device *vfe_dev); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c index 990654e7c560..4705a12bbcd1 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.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 @@ -256,13 +256,12 @@ static int32_t msm_isp_stats_buf_divert(struct vfe_device *vfe_dev, static int32_t msm_isp_stats_configure(struct vfe_device *vfe_dev, uint32_t stats_irq_mask, struct msm_isp_timestamp *ts, - bool is_composite) + uint32_t pingpong_status, bool is_composite) { int i, rc = 0; struct msm_isp_event_data buf_event; struct msm_isp_stats_event *stats_event = &buf_event.u.stats; struct msm_vfe_stats_stream *stream_info = NULL; - uint32_t pingpong_status; uint32_t comp_stats_type_mask = 0; int result = 0; @@ -271,8 +270,6 @@ static int32_t msm_isp_stats_configure(struct vfe_device *vfe_dev, buf_event.mono_timestamp = ts->buf_time; buf_event.frame_id = vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id; - pingpong_status = vfe_dev->hw_info-> - vfe_ops.stats_ops.get_pingpong_status(vfe_dev); for (i = 0; i < vfe_dev->hw_info->stats_hw_info->num_stats_type; i++) { if (!(stats_irq_mask & (1 << i))) @@ -309,7 +306,7 @@ static int32_t msm_isp_stats_configure(struct vfe_device *vfe_dev, void msm_isp_process_stats_irq(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, - struct msm_isp_timestamp *ts) + uint32_t pingpong_status, struct msm_isp_timestamp *ts) { int j, rc; uint32_t atomic_stats_mask = 0; @@ -337,7 +334,7 @@ void msm_isp_process_stats_irq(struct vfe_device *vfe_dev, /* Process non-composite irq */ if (stats_irq_mask) { rc = msm_isp_stats_configure(vfe_dev, stats_irq_mask, ts, - comp_flag); + pingpong_status, comp_flag); } /* Process composite irq */ @@ -350,7 +347,7 @@ void msm_isp_process_stats_irq(struct vfe_device *vfe_dev, &vfe_dev->stats_data.stats_comp_mask[j]); rc = msm_isp_stats_configure(vfe_dev, atomic_stats_mask, - ts, !comp_flag); + ts, pingpong_status, !comp_flag); } } } @@ -1105,6 +1102,7 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl, struct vfe_device *vfe_dev; msm_isp_get_timestamp(×tamp, vfe_dev_ioctl); + mutex_lock(&vfe_dev_ioctl->buf_mgr->lock); num_stats_comp_mask = vfe_dev_ioctl->hw_info->stats_hw_info->num_stats_comp_mask; @@ -1123,6 +1121,7 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl, } if (rc) { spin_unlock_irqrestore(&stream_info->lock, flags); + mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); goto error; } rc = msm_isp_init_stats_ping_pong_reg( @@ -1130,6 +1129,7 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl, if (rc < 0) { spin_unlock_irqrestore(&stream_info->lock, flags); pr_err("%s: No buffer for stream%d\n", __func__, idx); + mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); return rc; } init_completion(&stream_info->active_comp); @@ -1164,6 +1164,7 @@ static int msm_isp_start_stats_stream(struct vfe_device *vfe_dev_ioctl, stats_data->num_active_stream); streams[num_stream++] = stream_info; } + mutex_unlock(&vfe_dev_ioctl->buf_mgr->lock); for (k = 0; k < MAX_VFE; k++) { if (!update_vfes[k] || num_active_streams[k]) diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h index 2e3a24dd1f0d..3efd5b57a029 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-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 @@ -17,7 +17,7 @@ void msm_isp_process_stats_irq(struct vfe_device *vfe_dev, uint32_t irq_status0, uint32_t irq_status1, - struct msm_isp_timestamp *ts); + uint32_t pingpong_status, struct msm_isp_timestamp *ts); void msm_isp_stats_stream_update(struct vfe_device *vfe_dev); int msm_isp_cfg_stats_stream(struct vfe_device *vfe_dev, void *arg); int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg); diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index 92b1f2ea871b..521bac5580c9 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.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 @@ -404,8 +404,10 @@ static int msm_isp_start_fetch_engine_multi_pass(struct vfe_device *vfe_dev, vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); msm_isp_reset_framedrop(vfe_dev, stream_info); + mutex_lock(&vfe_dev->buf_mgr->lock); rc = msm_isp_cfg_offline_ping_pong_address(vfe_dev, stream_info, VFE_PING_FLAG, fe_cfg->output_buf_idx); + mutex_unlock(&vfe_dev->buf_mgr->lock); if (rc < 0) { pr_err("%s: Fetch engine config failed\n", __func__); return -EINVAL; @@ -923,6 +925,7 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd, case VIDIOC_MSM_ISP_AXI_RESTART: mutex_lock(&vfe_dev->core_mutex); MSM_ISP_DUAL_VFE_MUTEX_LOCK(vfe_dev); + mutex_lock(&vfe_dev->buf_mgr->lock); if (atomic_read(&vfe_dev->error_info.overflow_state) != HALT_ENFORCED) { rc = msm_isp_stats_restart(vfe_dev); @@ -933,6 +936,7 @@ static long msm_isp_ioctl_unlocked(struct v4l2_subdev *sd, pr_err_ratelimited("%s: no AXI restart, halt enforced.\n", __func__); } + mutex_unlock(&vfe_dev->buf_mgr->lock); MSM_ISP_DUAL_VFE_MUTEX_UNLOCK(vfe_dev); mutex_unlock(&vfe_dev->core_mutex); break; @@ -1760,6 +1764,7 @@ int msm_isp_get_bit_per_pixel(uint32_t output_format) case V4L2_PIX_FMT_QGRBG12: case V4L2_PIX_FMT_QRGGB12: case V4L2_PIX_FMT_Y12: + case MSM_V4L2_PIX_FMT_META12: return 12; case V4L2_PIX_FMT_SBGGR14: case V4L2_PIX_FMT_SGBRG14: @@ -2061,7 +2066,8 @@ void msm_isp_prepare_tasklet_debug_info(struct vfe_device *vfe_dev, } static void msm_isp_enqueue_tasklet_cmd(struct vfe_device *vfe_dev, - uint32_t irq_status0, uint32_t irq_status1) + uint32_t irq_status0, uint32_t irq_status1, + uint32_t ping_pong_status) { unsigned long flags; struct msm_vfe_tasklet_queue_cmd *queue_cmd = NULL; @@ -2084,8 +2090,8 @@ static void msm_isp_enqueue_tasklet_cmd(struct vfe_device *vfe_dev, } queue_cmd->vfeInterruptStatus0 = irq_status0; queue_cmd->vfeInterruptStatus1 = irq_status1; + queue_cmd->vfe_pingpong_status = ping_pong_status; msm_isp_get_timestamp(&queue_cmd->ts, vfe_dev); - queue_cmd->cmd_used = 1; queue_cmd->vfe_dev = vfe_dev; @@ -2099,7 +2105,7 @@ static void msm_isp_enqueue_tasklet_cmd(struct vfe_device *vfe_dev, irqreturn_t msm_isp_process_irq(int irq_num, void *data) { struct vfe_device *vfe_dev = (struct vfe_device *) data; - uint32_t irq_status0, irq_status1; + uint32_t irq_status0, irq_status1, ping_pong_status; uint32_t error_mask0, error_mask1; vfe_dev->hw_info->vfe_ops.irq_ops. @@ -2110,6 +2116,8 @@ irqreturn_t msm_isp_process_irq(int irq_num, void *data) __func__, vfe_dev->pdev->id); return IRQ_HANDLED; } + ping_pong_status = vfe_dev->hw_info->vfe_ops.axi_ops. + get_pingpong_status(vfe_dev); if (vfe_dev->hw_info->vfe_ops.irq_ops.preprocess_camif_irq) { vfe_dev->hw_info->vfe_ops.irq_ops.preprocess_camif_irq( vfe_dev, irq_status0); @@ -2137,7 +2145,8 @@ irqreturn_t msm_isp_process_irq(int irq_num, void *data) return IRQ_HANDLED; } msm_isp_prepare_irq_debug_info(vfe_dev, irq_status0, irq_status1); - msm_isp_enqueue_tasklet_cmd(vfe_dev, irq_status0, irq_status1); + msm_isp_enqueue_tasklet_cmd(vfe_dev, irq_status0, irq_status1, + ping_pong_status); return IRQ_HANDLED; } @@ -2150,7 +2159,7 @@ void msm_isp_do_tasklet(unsigned long data) struct msm_vfe_irq_ops *irq_ops; struct msm_vfe_tasklet_queue_cmd *queue_cmd; struct msm_isp_timestamp ts; - uint32_t irq_status0, irq_status1; + uint32_t irq_status0, irq_status1, pingpong_status; while (1) { spin_lock_irqsave(&tasklet->tasklet_lock, flags); @@ -2166,6 +2175,7 @@ void msm_isp_do_tasklet(unsigned long data) queue_cmd->vfe_dev = NULL; irq_status0 = queue_cmd->vfeInterruptStatus0; irq_status1 = queue_cmd->vfeInterruptStatus1; + pingpong_status = queue_cmd->vfe_pingpong_status; ts = queue_cmd->ts; spin_unlock_irqrestore(&tasklet->tasklet_lock, flags); if (vfe_dev->vfe_open_cnt == 0) { @@ -2190,9 +2200,11 @@ void msm_isp_do_tasklet(unsigned long data) } msm_isp_process_error_info(vfe_dev); irq_ops->process_stats_irq(vfe_dev, - irq_status0, irq_status1, &ts); + irq_status0, irq_status1, + pingpong_status, &ts); irq_ops->process_axi_irq(vfe_dev, - irq_status0, irq_status1, &ts); + irq_status0, irq_status1, + pingpong_status, &ts); irq_ops->process_camif_irq(vfe_dev, irq_status0, irq_status1, &ts); irq_ops->process_reg_update(vfe_dev, diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c index 06e3ee4c353b..5ef08cbe9aee 100644 --- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.c +++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_dev.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 @@ -32,6 +32,8 @@ #define MSM_JPEG_NAME "jpeg" #define DEV_NAME_LEN 10 +static char devname[DEV_NAME_LEN]; + static int msm_jpeg_open(struct inode *inode, struct file *filp) { int rc = 0; @@ -185,7 +187,6 @@ static int msm_jpeg_init_dev(struct platform_device *pdev) struct msm_jpeg_device *msm_jpeg_device_p; const struct of_device_id *device_id; const struct msm_jpeg_priv_data *priv_data; - char devname[DEV_NAME_LEN]; msm_jpeg_device_p = kzalloc(sizeof(struct msm_jpeg_device), GFP_ATOMIC); if (!msm_jpeg_device_p) { @@ -328,6 +329,7 @@ static struct platform_driver msm_jpeg_driver = { static int __init msm_jpeg_driver_init(void) { int rc; + rc = platform_driver_register(&msm_jpeg_driver); return rc; } diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c index 25fc34b26bc1..dc0aba13e603 100644 --- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c +++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.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 @@ -958,9 +958,14 @@ static irqreturn_t msm_cpp_irq(int irq_num, void *data) if (irq_status & 0x8) { tx_level = msm_camera_io_r(cpp_dev->base + MSM_CPP_MICRO_FIFO_TX_STAT) >> 2; - for (i = 0; i < tx_level; i++) { - tx_fifo[i] = msm_camera_io_r(cpp_dev->base + - MSM_CPP_MICRO_FIFO_TX_DATA); + if (tx_level < MSM_CPP_TX_FIFO_LEVEL) { + for (i = 0; i < tx_level; i++) { + tx_fifo[i] = msm_camera_io_r(cpp_dev->base + + MSM_CPP_MICRO_FIFO_TX_DATA); + } + } else { + pr_err("Fatal invalid tx level %d", tx_level); + goto err; } spin_lock_irqsave(&cpp_dev->tasklet_lock, flags); queue_cmd = &cpp_dev->tasklet_queue_cmd[cpp_dev->taskletq_idx]; @@ -1015,6 +1020,7 @@ static irqreturn_t msm_cpp_irq(int irq_num, void *data) pr_debug("DEBUG_R1: 0x%x\n", msm_camera_io_r(cpp_dev->base + 0x8C)); } +err: msm_camera_io_w(irq_status, cpp_dev->base + MSM_CPP_MICRO_IRQGEN_CLR); return IRQ_HANDLED; } diff --git a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c index b8c8bc2ec3ec..ab996c970f28 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c +++ b/drivers/media/platform/msm/camera_v2/sensor/actuator/msm_actuator.c @@ -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 @@ -641,6 +641,8 @@ static int32_t msm_actuator_move_focus( a_ctrl->curr_step_pos, dest_step_pos, curr_lens_pos); while (a_ctrl->curr_step_pos != dest_step_pos) { + if (a_ctrl->curr_region_index >= a_ctrl->region_size) + break; step_boundary = a_ctrl->region_params[a_ctrl->curr_region_index]. step_bound[dir]; diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c index 8d091320cbca..57bc2cfcb6bf 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c @@ -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 @@ -52,6 +52,7 @@ #define MAX_DPHY_DATA_LN 4 #define CLOCK_OFFSET 0x700 #define CSIPHY_SOF_DEBUG_COUNT 2 +#define GBPS 1000000000 #undef CDBG #define CDBG(fmt, args...) pr_debug(fmt, ##args) @@ -134,8 +135,10 @@ static int msm_csiphy_3phase_lane_config( uint8_t i = 0; uint16_t lane_mask = 0, lane_enable = 0, temp; void __iomem *csiphybase; + uint64_t two_gbps = 0; csiphybase = csiphy_dev->base; + two_gbps = 2 * (uint64_t)csiphy_params->lane_cnt * GBPS; lane_mask = csiphy_params->lane_mask & 0x7; while (lane_mask != 0) { temp = (i << 1)+1; @@ -281,11 +284,20 @@ static int msm_csiphy_3phase_lane_config( csiphy_3ph_reg.mipi_csiphy_3ph_lnn_ctrl51.addr + 0x200*i); } - msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. - mipi_csiphy_3ph_lnn_ctrl25.data, - csiphybase + csiphy_dev->ctrl_reg->csiphy_3ph_reg. - mipi_csiphy_3ph_lnn_ctrl25.addr + 0x200*i); + if ((csiphy_dev->hw_version == CSIPHY_VERSION_V35) && + (csiphy_params->data_rate > two_gbps)) { + msm_camera_io_w(0x40, + csiphybase + + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl25.addr + 0x200*i); + } else { + msm_camera_io_w(csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl25.data, + csiphybase + + csiphy_dev->ctrl_reg->csiphy_3ph_reg. + mipi_csiphy_3ph_lnn_ctrl25.addr + 0x200*i); + } lane_mask >>= 1; i++; } @@ -782,10 +794,10 @@ static int msm_csiphy_lane_config(struct csiphy_device *csiphy_dev, ratio = csiphy_dev->csiphy_max_clk/clk_rate; csiphy_params->settle_cnt = csiphy_params->settle_cnt/ratio; } - CDBG("%s csiphy_params, mask = 0x%x cnt = %d\n", + CDBG("%s csiphy_params, mask = 0x%x cnt = %d, data rate = %llu\n", __func__, csiphy_params->lane_mask, - csiphy_params->lane_cnt); + csiphy_params->lane_cnt, csiphy_params->data_rate); CDBG("%s csiphy_params, settle cnt = 0x%x csid %d\n", __func__, csiphy_params->settle_cnt, csiphy_params->csid_core); diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c index 1966fa9805c0..a2381557070d 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.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 @@ -482,6 +482,11 @@ static ssize_t sde_rot_evtlog_dump_read(struct file *file, char __user *buff, if (__sde_rot_evtlog_dump_calc_range()) { len = sde_rot_evtlog_dump_entry(evtlog_buf, SDE_ROT_EVTLOG_BUF_MAX); + if (len < 0 || len > count) { + pr_err("len is more than the user buffer size\n"); + return 0; + } + if (copy_to_user(buff, evtlog_buf, len)) return -EFAULT; *ppos += len; diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c index 08bbed147c86..76e1b60512d0 100644 --- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c +++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c @@ -2009,6 +2009,18 @@ static long sde_rotator_compat_ioctl32(struct file *file, } #endif +static int sde_rotator_ctrl_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + return -EINVAL; +} + +static int sde_rotator_event_unsubscribe(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + return -EINVAL; +} + /* V4l2 ioctl handlers */ static const struct v4l2_ioctl_ops sde_rotator_ioctl_ops = { .vidioc_querycap = sde_rotator_querycap, @@ -2033,8 +2045,8 @@ static const struct v4l2_ioctl_ops sde_rotator_ioctl_ops = { .vidioc_s_parm = sde_rotator_s_parm, .vidioc_default = sde_rotator_private_ioctl, .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_subscribe_event = sde_rotator_ctrl_subscribe_event, + .vidioc_unsubscribe_event = sde_rotator_event_unsubscribe, }; /* diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c index 76945c43ed56..7204d8e8401c 100644 --- a/drivers/media/platform/msm/vidc/msm_vdec.c +++ b/drivers/media/platform/msm/vidc/msm_vdec.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 @@ -24,6 +24,7 @@ #define MSM_VDEC_DVC_NAME "msm_vdec_8974" #define MIN_NUM_OUTPUT_BUFFERS 4 #define MIN_NUM_OUTPUT_BUFFERS_VP9 6 +#define MIN_NUM_OUTPUT_BUFFERS_HEVC 5 #define MIN_NUM_CAPTURE_BUFFERS 6 #define MIN_NUM_THUMBNAIL_MODE_CAPTURE_BUFFERS 1 #define MAX_NUM_OUTPUT_BUFFERS VB2_MAX_FRAME @@ -1479,6 +1480,10 @@ static int msm_vdec_queue_setup(struct vb2_queue *q, V4L2_PIX_FMT_VP9 && *num_buffers < MIN_NUM_OUTPUT_BUFFERS_VP9) *num_buffers = MIN_NUM_OUTPUT_BUFFERS_VP9; + else if (inst->fmts[OUTPUT_PORT].fourcc == + V4L2_PIX_FMT_HEVC && + *num_buffers < MIN_NUM_OUTPUT_BUFFERS_HEVC) + *num_buffers = MIN_NUM_OUTPUT_BUFFERS_HEVC; for (i = 0; i < *num_planes; i++) { sizes[i] = get_frame_size(inst, @@ -1638,6 +1643,8 @@ static int set_max_internal_buffers_size(struct msm_vidc_inst *inst) get_buff_req_buffer(inst, internal_buffers[i].type); internal_buffers[i].size = internal_buffers[i].req ? internal_buffers[i].req->buffer_size : 0; + if (internal_buffers[i].req == NULL) + continue; rc = allocate_and_set_internal_bufs(inst, internal_buffers[i].req, diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c index 9e07fb8f81a5..9ebb28d347dd 100644 --- a/drivers/media/platform/msm/vidc/msm_vidc_common.c +++ b/drivers/media/platform/msm/vidc/msm_vidc_common.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 @@ -1135,8 +1135,7 @@ static void handle_event_change(enum hal_command_response cmd, void *data) case HAL_EVENT_SEQ_CHANGED_SUFFICIENT_RESOURCES: rc = msm_comm_g_ctrl_for_id(inst, V4L2_CID_MPEG_VIDC_VIDEO_CONTINUE_DATA_TRANSFER); - if (!is_thumbnail_session(inst) && - (IS_ERR_VALUE(rc) || rc == false)) + if ((IS_ERR_VALUE(rc) || rc == false)) event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT; else event = V4L2_EVENT_SEQ_CHANGED_SUFFICIENT; @@ -3117,7 +3116,7 @@ static int set_output_buffers(struct msm_vidc_inst *inst, { int rc = 0; struct msm_smem *handle; - struct internal_buf *binfo; + struct internal_buf *binfo = NULL; u32 smem_flags = 0, buffer_size; struct hal_buffer_requirements *output_buf, *extradata_buf; int i; @@ -3223,10 +3222,10 @@ static int set_output_buffers(struct msm_vidc_inst *inst, } return rc; fail_set_buffers: - kfree(binfo); -fail_kzalloc: msm_comm_smem_free(inst, handle); err_no_mem: + kfree(binfo); +fail_kzalloc: return rc; } diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 9db0dac938d3..8a6d6875a178 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -20,6 +20,13 @@ #include #include +#define convert_in_user(srcptr, dstptr) \ +({ \ + typeof(*srcptr) val; \ + \ + get_user(val, srcptr) || put_user(val, dstptr); \ +}) + static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { long ret = -ENOIOCTLCMD; @@ -45,27 +52,45 @@ struct v4l2_window32 { compat_caddr_t bitmap; }; -static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up) +static int bufsize_v4l2_window32(struct v4l2_window32 __user *up) { + __u32 clipcount; + + if (get_user(clipcount, &up->clipcount)) + return -EFAULT; + if (clipcount > 2048) + return -EINVAL; + return clipcount * sizeof(struct v4l2_clip); +} + +static int get_v4l2_window32(struct v4l2_window __user *kp, struct + v4l2_window32 __user *up, void __user *aux_buf, int aux_space) +{ + __u32 clipcount; + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_window32)) || - copy_from_user(&kp->w, &up->w, sizeof(up->w)) || - get_user(kp->field, &up->field) || - get_user(kp->chromakey, &up->chromakey) || - get_user(kp->clipcount, &up->clipcount)) + copy_in_user(&kp->w, &up->w, sizeof(up->w)) || + convert_in_user(&up->field, &kp->field) || + convert_in_user(&up->chromakey, &kp->chromakey) || + get_user(clipcount, &up->clipcount) || + put_user(clipcount, &kp->clipcount)) return -EFAULT; - if (kp->clipcount > 2048) + if (clipcount > 2048) return -EINVAL; - if (kp->clipcount) { + if (clipcount) { struct v4l2_clip32 __user *uclips; struct v4l2_clip __user *kclips; - int n = kp->clipcount; + int n = clipcount; compat_caddr_t p; if (get_user(p, &up->clips)) return -EFAULT; uclips = compat_ptr(p); - kclips = compat_alloc_user_space(n * sizeof(struct v4l2_clip)); - kp->clips = kclips; + if (aux_space < n * sizeof(struct v4l2_clip)) + return -EFAULT; + kclips = aux_buf; + if (put_user(kclips, &kp->clips)) + return -EFAULT; while (--n >= 0) { if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c))) return -EFAULT; @@ -74,89 +99,91 @@ static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user uclips += 1; kclips += 1; } - } else - kp->clips = NULL; + } else { + if (put_user(NULL, &kp->clips)) + return -EFAULT; + } return 0; } -static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up) +static int put_v4l2_window32(struct v4l2_window __user *kp, struct v4l2_window32 __user *up) { - if (copy_to_user(&up->w, &kp->w, sizeof(kp->w)) || - put_user(kp->field, &up->field) || - put_user(kp->chromakey, &up->chromakey) || - put_user(kp->clipcount, &up->clipcount)) - return -EFAULT; + if (copy_in_user(&up->w, &kp->w, sizeof(kp->w)) || + convert_in_user(&kp->field, &up->field) || + convert_in_user(&kp->chromakey, &up->chromakey) || + convert_in_user(&kp->clipcount, &up->clipcount)) + return -EFAULT; return 0; } -static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up) +static inline int get_v4l2_pix_format(struct v4l2_pix_format __user *kp, struct v4l2_pix_format __user *up) { - if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format))) + if (copy_in_user(kp, up, sizeof(struct v4l2_pix_format))) return -EFAULT; return 0; } -static inline int get_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp, +static inline int get_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane __user *kp, struct v4l2_pix_format_mplane __user *up) { - if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format_mplane))) + if (copy_in_user(kp, up, sizeof(struct v4l2_pix_format_mplane))) return -EFAULT; return 0; } -static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up) +static inline int put_v4l2_pix_format(struct v4l2_pix_format __user *kp, struct v4l2_pix_format __user *up) { - if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format))) + if (copy_in_user(up, kp, sizeof(struct v4l2_pix_format))) return -EFAULT; return 0; } -static inline int put_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp, +static inline int put_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane __user *kp, struct v4l2_pix_format_mplane __user *up) { - if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format_mplane))) + if (copy_in_user(up, kp, sizeof(struct v4l2_pix_format_mplane))) return -EFAULT; return 0; } -static inline int get_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up) +static inline int get_v4l2_vbi_format(struct v4l2_vbi_format __user *kp, struct v4l2_vbi_format __user *up) { - if (copy_from_user(kp, up, sizeof(struct v4l2_vbi_format))) + if (copy_in_user(kp, up, sizeof(struct v4l2_vbi_format))) return -EFAULT; return 0; } -static inline int put_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up) +static inline int put_v4l2_vbi_format(struct v4l2_vbi_format __user *kp, struct v4l2_vbi_format __user *up) { - if (copy_to_user(up, kp, sizeof(struct v4l2_vbi_format))) + if (copy_in_user(up, kp, sizeof(struct v4l2_vbi_format))) return -EFAULT; return 0; } -static inline int get_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up) +static inline int get_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format __user *kp, struct v4l2_sliced_vbi_format __user *up) { - if (copy_from_user(kp, up, sizeof(struct v4l2_sliced_vbi_format))) + if (copy_in_user(kp, up, sizeof(struct v4l2_sliced_vbi_format))) return -EFAULT; return 0; } -static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up) +static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format __user *kp, struct v4l2_sliced_vbi_format __user *up) { - if (copy_to_user(up, kp, sizeof(struct v4l2_sliced_vbi_format))) + if (copy_in_user(up, kp, sizeof(struct v4l2_sliced_vbi_format))) return -EFAULT; return 0; } -static inline int get_v4l2_sdr_format(struct v4l2_sdr_format *kp, struct v4l2_sdr_format __user *up) +static inline int get_v4l2_sdr_format(struct v4l2_sdr_format __user *kp, struct v4l2_sdr_format __user *up) { - if (copy_from_user(kp, up, sizeof(struct v4l2_sdr_format))) + if (copy_in_user(kp, up, sizeof(struct v4l2_sdr_format))) return -EFAULT; return 0; } -static inline int put_v4l2_sdr_format(struct v4l2_sdr_format *kp, struct v4l2_sdr_format __user *up) +static inline int put_v4l2_sdr_format(struct v4l2_sdr_format __user *kp, struct v4l2_sdr_format __user *up) { - if (copy_to_user(up, kp, sizeof(struct v4l2_sdr_format))) + if (copy_in_user(up, kp, sizeof(struct v4l2_sdr_format))) return -EFAULT; return 0; } @@ -191,12 +218,31 @@ struct v4l2_create_buffers32 { __u32 reserved[8]; }; -static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) +static int __bufsize_v4l2_format32(struct v4l2_format32 __user *up) +{ + __u32 type; + + if (get_user(type, &up->type)) + return -EFAULT; + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + return bufsize_v4l2_window32(&up->fmt.win); + default: + return 0; + } +} + +static int __get_v4l2_format32(struct v4l2_format __user *kp, struct + v4l2_format32 __user *up, void __user *aux_buf, int aux_space) { - if (get_user(kp->type, &up->type)) + __u32 type; + + if (get_user(type, &up->type) || put_user(type, &kp->type)) return -EFAULT; - switch (kp->type) { + switch (type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix); @@ -206,7 +252,7 @@ static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us &up->fmt.pix_mp); case V4L2_BUF_TYPE_VIDEO_OVERLAY: case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - return get_v4l2_window32(&kp->fmt.win, &up->fmt.win); + return get_v4l2_window32(&kp->fmt.win, &up->fmt.win, aux_buf, aux_space); case V4L2_BUF_TYPE_VBI_CAPTURE: case V4L2_BUF_TYPE_VBI_OUTPUT: return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi); @@ -223,27 +269,51 @@ static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us } } -static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) +static int bufsize_v4l2_format32(struct v4l2_format32 __user *up) { if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32))) return -EFAULT; - return __get_v4l2_format32(kp, up); + return __bufsize_v4l2_format32(up); +} + +static int get_v4l2_format32(struct v4l2_format __user *kp, struct + v4l2_format32 __user *up, void __user *aux_buf, int aux_space) +{ + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32))) + return -EFAULT; + return __get_v4l2_format32(kp, up, aux_buf, aux_space); +} + +static int bufsize_v4l2_create32(struct v4l2_create_buffers32 __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32))) + return -EFAULT; + return __bufsize_v4l2_format32(&up->format); } -static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up) +static int get_v4l2_create32(struct v4l2_create_buffers __user *kp, struct + v4l2_create_buffers32 __user *up, void __user *aux_buf, + int aux_space) { if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) || - copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format))) + copy_in_user(kp, up, offsetof(struct v4l2_create_buffers32, format))) return -EFAULT; - return __get_v4l2_format32(&kp->format, &up->format); + return __get_v4l2_format32(&kp->format, &up->format, aux_buf, aux_space); } -static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) +static int __put_v4l2_format32(struct v4l2_format __user *kp, struct v4l2_format32 __user *up) { - if (put_user(kp->type, &up->type)) + __u32 type; + + if (kp == NULL) return -EFAULT; - switch (kp->type) { + if (get_user(type, &kp->type)) + return -EFAULT; + if (put_user(type, &up->type)) + return -EFAULT; + + switch (type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix); @@ -270,18 +340,18 @@ static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us } } -static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) +static int put_v4l2_format32(struct v4l2_format __user *kp, struct v4l2_format32 __user *up) { if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32))) return -EFAULT; return __put_v4l2_format32(kp, up); } -static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up) +static int put_v4l2_create32(struct v4l2_create_buffers __user *kp, struct v4l2_create_buffers32 __user *up) { if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) || - copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format)) || - copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved))) + copy_in_user(up, kp, offsetof(struct v4l2_create_buffers32, format)) || + copy_in_user(up->reserved, kp->reserved, sizeof(kp->reserved))) return -EFAULT; return __put_v4l2_format32(&kp->format, &up->format); } @@ -295,24 +365,24 @@ struct v4l2_standard32 { __u32 reserved[4]; }; -static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up) +static int get_v4l2_standard32(struct v4l2_standard __user *kp, struct v4l2_standard32 __user *up) { /* other fields are not set by the user, nor used by the driver */ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_standard32)) || - get_user(kp->index, &up->index)) + convert_in_user(&up->index, &kp->index)) return -EFAULT; return 0; } -static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up) +static int put_v4l2_standard32(struct v4l2_standard __user *kp, struct v4l2_standard32 __user *up) { if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) || - put_user(kp->index, &up->index) || - put_user(kp->id, &up->id) || - copy_to_user(up->name, kp->name, 24) || - copy_to_user(&up->frameperiod, &kp->frameperiod, sizeof(kp->frameperiod)) || - put_user(kp->framelines, &up->framelines) || - copy_to_user(up->reserved, kp->reserved, 4 * sizeof(__u32))) + convert_in_user(&kp->index, &up->index) || + copy_in_user((void __user *)up->id, &kp->id, sizeof(__u64)) || + copy_in_user(up->name, kp->name, 24) || + copy_in_user(&up->frameperiod, &kp->frameperiod, sizeof(kp->frameperiod)) || + convert_in_user(&kp->framelines, &up->framelines) || + copy_in_user(up->reserved, kp->reserved, 4 * sizeof(__u32))) return -EFAULT; return 0; } @@ -355,7 +425,6 @@ struct v4l2_buffer32 { static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32, enum v4l2_memory memory) { - void __user *up_pln; compat_long_t p; if (copy_in_user(up, up32, 2 * sizeof(__u32)) || @@ -368,10 +437,9 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __ return -EFAULT; if (memory == V4L2_MEMORY_USERPTR) { - if (get_user(p, &up32->m.userptr)) - return -EFAULT; - up_pln = compat_ptr(p); - if (put_user((unsigned long)up_pln, &up->m.userptr)) + if (get_user(p, &up32->m.userptr) || + put_user((unsigned long) compat_ptr(p), + &up->m.userptr)) return -EFAULT; } else if (memory == V4L2_MEMORY_DMABUF) { if (copy_in_user(&up->m.fd, &up32->m.fd, sizeof(int))) @@ -414,8 +482,34 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __ return 0; } -static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up) +static int bufsize_v4l2_buffer32(struct v4l2_buffer32 __user *up) +{ + __u32 type; + __u32 length; + + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_buffer32)) || + get_user(type, &up->type) || + get_user(length, &up->length)) + return -EFAULT; + + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + if (length > VIDEO_MAX_PLANES) + return -EINVAL; + + /* We don't really care if userspace decides to kill itself + * by passing a very big length value + */ + return length * sizeof(struct v4l2_plane); + } + return 0; +} + +static int get_v4l2_buffer32(struct v4l2_buffer __user *kp, struct + v4l2_buffer32 __user *up, void __user *aux_buf, int aux_space) { + __u32 type; + __u32 length; + enum v4l2_memory memory; struct v4l2_plane32 __user *uplane32; struct v4l2_plane __user *uplane; compat_caddr_t p; @@ -423,28 +517,39 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user int ret; if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_buffer32)) || - get_user(kp->index, &up->index) || - get_user(kp->type, &up->type) || - get_user(kp->flags, &up->flags) || - get_user(kp->memory, &up->memory) || - get_user(kp->length, &up->length)) + convert_in_user(&up->index, &kp->index) || + get_user(type, &up->type) || + put_user(type, &kp->type) || + convert_in_user(&up->flags, &kp->flags) || + get_user(memory, &up->memory) || + put_user(memory, &kp->memory) || + convert_in_user(&up->length, &kp->length) || + get_user(length, &up->length) || + put_user(length, &kp->length)) return -EFAULT; - if (V4L2_TYPE_IS_OUTPUT(kp->type)) - if (get_user(kp->bytesused, &up->bytesused) || - get_user(kp->field, &up->field) || - get_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) || - get_user(kp->timestamp.tv_usec, - &up->timestamp.tv_usec)) + if (V4L2_TYPE_IS_OUTPUT(type)) + if (convert_in_user(&up->bytesused, &kp->bytesused) || + convert_in_user(&up->field, &kp->field) || + convert_in_user(&up->timestamp.tv_sec, &kp->timestamp.tv_sec) || + convert_in_user(&up->timestamp.tv_usec, + &kp->timestamp.tv_usec)) return -EFAULT; - if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) { - num_planes = kp->length; + if (type == V4L2_BUF_TYPE_PRIVATE) { + compat_long_t tmp; + + if (get_user(tmp, &up->m.userptr) || + put_user((unsigned long) compat_ptr(tmp), + &kp->m.userptr)) + return put_user(NULL, &kp->m.planes);; + } + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + num_planes = length; if (num_planes == 0) { - kp->m.planes = NULL; /* num_planes == 0 is legal, e.g. when userspace doesn't * need planes array on DQBUF*/ - return 0; + return put_user(NULL, &kp->m.planes); } if (get_user(p, &up->m.planes)) @@ -457,39 +562,44 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user /* We don't really care if userspace decides to kill itself * by passing a very big num_planes value */ - uplane = compat_alloc_user_space(num_planes * - sizeof(struct v4l2_plane)); - kp->m.planes = (__force struct v4l2_plane *)uplane; + if (aux_space < num_planes * sizeof(struct v4l2_plane)) + return -EFAULT; + + uplane = aux_buf; + if (put_user((__force struct v4l2_plane *)uplane, + &kp->m.planes)) + return -EFAULT; while (--num_planes >= 0) { - ret = get_v4l2_plane32(uplane, uplane32, kp->memory); + ret = get_v4l2_plane32(uplane, uplane32, memory); if (ret) return ret; ++uplane; ++uplane32; } } else { - switch (kp->memory) { + switch (memory) { case V4L2_MEMORY_MMAP: - if (get_user(kp->m.offset, &up->m.offset)) + if (convert_in_user(&up->m.offset, &kp->m.offset)) return -EFAULT; break; case V4L2_MEMORY_USERPTR: { - compat_long_t tmp; + compat_long_t tmp; - if (get_user(tmp, &up->m.userptr)) - return -EFAULT; - - kp->m.userptr = (unsigned long)compat_ptr(tmp); + if (get_user(tmp, &up->m.userptr) || + put_user((unsigned long) + compat_ptr(tmp), + &kp->m.userptr)) + return -EFAULT; } break; case V4L2_MEMORY_OVERLAY: - if (get_user(kp->m.offset, &up->m.offset)) + if (convert_in_user(&up->m.offset, &kp->m.offset)) return -EFAULT; break; case V4L2_MEMORY_DMABUF: - if (get_user(kp->m.fd, &up->m.fd)) + if (convert_in_user(&up->m.fd, &kp->m.fd)) return -EFAULT; break; } @@ -498,8 +608,11 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user return 0; } -static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up) +static int put_v4l2_buffer32(struct v4l2_buffer __user *kp, struct v4l2_buffer32 __user *up) { + __u32 type; + __u32 length; + enum v4l2_memory memory; struct v4l2_plane32 __user *uplane32; struct v4l2_plane __user *uplane; compat_caddr_t p; @@ -507,56 +620,63 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user int ret; if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_buffer32)) || - put_user(kp->index, &up->index) || - put_user(kp->type, &up->type) || - put_user(kp->flags, &up->flags) || - put_user(kp->memory, &up->memory)) + convert_in_user(&kp->index, &up->index) || + get_user(type, &kp->type) || + put_user(type, &up->type) || + convert_in_user(&kp->flags, &up->flags) || + get_user(memory, &kp->memory) || + put_user(memory, &up->memory)) return -EFAULT; - if (put_user(kp->bytesused, &up->bytesused) || - put_user(kp->field, &up->field) || - put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) || - put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec) || - copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) || - put_user(kp->sequence, &up->sequence) || - put_user(kp->reserved2, &up->reserved2) || - put_user(kp->reserved, &up->reserved) || - put_user(kp->length, &up->length)) + if (convert_in_user(&kp->bytesused, &up->bytesused) || + convert_in_user(&kp->field, &up->field) || + convert_in_user(&kp->timestamp.tv_sec, &up->timestamp.tv_sec) || + convert_in_user(&kp->timestamp.tv_usec, &up->timestamp.tv_usec) || + copy_in_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) || + convert_in_user(&kp->sequence, &up->sequence) || + convert_in_user(&kp->reserved2, &up->reserved2) || + convert_in_user(&kp->reserved, &up->reserved) || + get_user(length, &kp->length) || + put_user(length, &up->length)) + return -EFAULT; + if (type == V4L2_BUF_TYPE_PRIVATE) + if (convert_in_user(&kp->m.userptr, &up->m.userptr)) return -EFAULT; - if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) { - num_planes = kp->length; + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + num_planes = length; if (num_planes == 0) return 0; - uplane = (__force struct v4l2_plane __user *)kp->m.planes; + if (get_user(uplane, ((__force struct v4l2_plane __user **)&kp->m.planes))) + return -EFAULT; if (get_user(p, &up->m.planes)) return -EFAULT; uplane32 = compat_ptr(p); while (--num_planes >= 0) { - ret = put_v4l2_plane32(uplane, uplane32, kp->memory); + ret = put_v4l2_plane32(uplane, uplane32, memory); if (ret) return ret; ++uplane; ++uplane32; } } else { - switch (kp->memory) { + switch (memory) { case V4L2_MEMORY_MMAP: - if (put_user(kp->m.offset, &up->m.offset)) + if (convert_in_user(&kp->m.offset, &up->m.offset)) return -EFAULT; break; case V4L2_MEMORY_USERPTR: - if (put_user(kp->m.userptr, &up->m.userptr)) + if (convert_in_user(&kp->m.userptr, &up->m.userptr)) return -EFAULT; break; case V4L2_MEMORY_OVERLAY: - if (put_user(kp->m.offset, &up->m.offset)) + if (convert_in_user(&kp->m.offset, &up->m.offset)) return -EFAULT; break; case V4L2_MEMORY_DMABUF: - if (put_user(kp->m.fd, &up->m.fd)) + if (convert_in_user(&kp->m.fd, &up->m.fd)) return -EFAULT; break; } @@ -566,48 +686,50 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user } struct v4l2_framebuffer32 { - __u32 capability; - __u32 flags; - compat_caddr_t base; + __u32 capability; + __u32 flags; + compat_caddr_t base; struct { - __u32 width; - __u32 height; - __u32 pixelformat; - __u32 field; - __u32 bytesperline; - __u32 sizeimage; - __u32 colorspace; - __u32 priv; + __u32 width; + __u32 height; + __u32 pixelformat; + __u32 field; + __u32 bytesperline; + __u32 sizeimage; + __u32 colorspace; + __u32 priv; } fmt; }; -static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up) +static int get_v4l2_framebuffer32(struct v4l2_framebuffer __user *kp, struct v4l2_framebuffer32 __user *up) { - u32 tmp; + compat_caddr_t tmp; if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_framebuffer32)) || get_user(tmp, &up->base) || - get_user(kp->capability, &up->capability) || - get_user(kp->flags, &up->flags) || - copy_from_user(&kp->fmt, &up->fmt, sizeof(up->fmt))) + put_user((__force void *)compat_ptr(tmp), &kp->base) || + convert_in_user(&up->capability, &kp->capability) || + convert_in_user(&up->flags, &kp->flags) || + get_v4l2_pix_format((struct v4l2_pix_format *)&kp->fmt, (struct v4l2_pix_format *)&up->fmt)) return -EFAULT; - kp->base = (__force void *)compat_ptr(tmp); return 0; } -static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up) +static int put_v4l2_framebuffer32(struct v4l2_framebuffer __user *kp, struct v4l2_framebuffer32 __user *up) { - u32 tmp = (u32)((unsigned long)kp->base); + void *base; if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) || - put_user(tmp, &up->base) || - put_user(kp->capability, &up->capability) || - put_user(kp->flags, &up->flags) || - copy_to_user(&up->fmt, &kp->fmt, sizeof(up->fmt))) + get_user(base, &kp->base) || + put_user(ptr_to_compat(base), &up->base) || + convert_in_user(&kp->capability, &up->capability) || + convert_in_user(&kp->flags, &up->flags) || + put_v4l2_pix_format((struct v4l2_pix_format *)&kp->fmt, (struct v4l2_pix_format *)&up->fmt)) return -EFAULT; return 0; } + struct v4l2_input32 { __u32 index; /* Which input */ __u8 name[32]; /* Label */ @@ -621,16 +743,16 @@ struct v4l2_input32 { /* The 64-bit v4l2_input struct has extra padding at the end of the struct. Otherwise it is identical to the 32-bit version. */ -static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up) +static inline int get_v4l2_input32(struct v4l2_input __user *kp, struct v4l2_input32 __user *up) { - if (copy_from_user(kp, up, sizeof(struct v4l2_input32))) + if (copy_in_user(kp, up, sizeof(struct v4l2_input32))) return -EFAULT; return 0; } -static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up) +static inline int put_v4l2_input32(struct v4l2_input __user *kp, struct v4l2_input32 __user *up) { - if (copy_to_user(up, kp, sizeof(struct v4l2_input32))) + if (copy_in_user(up, kp, sizeof(struct v4l2_input32))) return -EFAULT; return 0; } @@ -671,34 +793,50 @@ static inline int ctrl_is_pointer(u32 id) } } -static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up) +static int bufsize_v4l2_ext_controls32(struct v4l2_ext_controls32 __user *up) +{ + __u32 count; + + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) || + get_user(count, &up->count)) + return -EFAULT; + if (count > V4L2_CID_MAX_CTRLS) + return -EINVAL; + return count * sizeof(struct v4l2_ext_control); +} + +static int get_v4l2_ext_controls32(struct v4l2_ext_controls __user *kp, struct + v4l2_ext_controls32 __user *up, void __user *aux_buf, + int aux_space) { struct v4l2_ext_control32 __user *ucontrols; struct v4l2_ext_control __user *kcontrols; - int n; + __u32 count; + unsigned int n; compat_caddr_t p; if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) || - get_user(kp->ctrl_class, &up->ctrl_class) || - get_user(kp->count, &up->count) || - get_user(kp->error_idx, &up->error_idx) || - copy_from_user(kp->reserved, up->reserved, - sizeof(kp->reserved))) + convert_in_user(&up->ctrl_class, &kp->ctrl_class) || + get_user(count, &up->count) || + put_user(count, &kp->count) || + convert_in_user(&up->error_idx, &kp->error_idx) || + copy_in_user(kp->reserved, up->reserved, sizeof(kp->reserved))) return -EFAULT; - n = kp->count; - if (n == 0) { - kp->controls = NULL; - return 0; - } + if (count == 0) + return put_user(NULL, &kp->controls); if (get_user(p, &up->controls)) return -EFAULT; ucontrols = compat_ptr(p); if (!access_ok(VERIFY_READ, ucontrols, - n * sizeof(struct v4l2_ext_control32))) + count * sizeof(struct v4l2_ext_control32))) + return -EFAULT; + if (aux_space < count * sizeof(struct v4l2_ext_control)) + return -EFAULT; + kcontrols = aux_buf; + if (put_user((__force struct v4l2_ext_control *)kcontrols, + &kp->controls)) return -EFAULT; - kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control)); - kp->controls = (__force struct v4l2_ext_control *)kcontrols; - while (--n >= 0) { + for (n = 0; n < count; n++) { u32 id; if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols))) @@ -720,31 +858,33 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext return 0; } -static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up) +static int put_v4l2_ext_controls32(struct v4l2_ext_controls __user *kp, struct v4l2_ext_controls32 __user *up) { struct v4l2_ext_control32 __user *ucontrols; - struct v4l2_ext_control __user *kcontrols = - (__force struct v4l2_ext_control __user *)kp->controls; - int n = kp->count; + struct v4l2_ext_control __user *kcontrols; + __u32 count; + unsigned int n; compat_caddr_t p; if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) || - put_user(kp->ctrl_class, &up->ctrl_class) || - put_user(kp->count, &up->count) || - put_user(kp->error_idx, &up->error_idx) || - copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved))) + get_user(kcontrols, &kp->controls) || + convert_in_user(&kp->ctrl_class, &up->ctrl_class) || + get_user(count, &kp->count) || + put_user(count, &up->count) || + convert_in_user(&kp->error_idx, &up->error_idx) || + copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved))) return -EFAULT; - if (!kp->count) + if (!count) return 0; if (get_user(p, &up->controls)) return -EFAULT; ucontrols = compat_ptr(p); if (!access_ok(VERIFY_WRITE, ucontrols, - n * sizeof(struct v4l2_ext_control32))) + count * sizeof(struct v4l2_ext_control32))) return -EFAULT; - while (--n >= 0) { + for (n = 0; n < count; n++) { unsigned size = sizeof(*ucontrols); u32 id; @@ -784,13 +924,14 @@ struct v4l2_event32 { static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *up) { if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_event32)) || - put_user(kp->type, &up->type) || - copy_to_user(&up->u, &kp->u, sizeof(kp->u)) || - put_user(kp->pending, &up->pending) || - put_user(kp->sequence, &up->sequence) || - compat_put_timespec(&kp->timestamp, &up->timestamp) || - put_user(kp->id, &up->id) || - copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32))) + convert_in_user(&kp->type, &up->type) || + copy_in_user(&up->u, &kp->u, sizeof(kp->u)) || + convert_in_user(&kp->pending, &up->pending) || + convert_in_user(&kp->sequence, &up->sequence) || + convert_in_user(&kp->timestamp.tv_sec, &up->timestamp.tv_sec) || + convert_in_user(&kp->timestamp.tv_nsec, &up->timestamp.tv_nsec) || + convert_in_user(&kp->id, &up->id) || + copy_in_user(up->reserved, kp->reserved, 8 * sizeof(__u32))) return -EFAULT; return 0; } @@ -803,31 +944,34 @@ struct v4l2_edid32 { compat_caddr_t edid; }; -static int get_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up) +#define v4l2_subdev_edid32 v4l2_edid32 + +static int get_v4l2_subdev_edid32(struct v4l2_subdev_edid __user *kp, struct v4l2_subdev_edid32 __user *up) { - u32 tmp; + compat_uptr_t tmp; - if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_edid32)) || - get_user(kp->pad, &up->pad) || - get_user(kp->start_block, &up->start_block) || - get_user(kp->blocks, &up->blocks) || + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_subdev_edid32)) || + convert_in_user(&up->pad, &kp->pad) || + convert_in_user(&up->start_block, &kp->start_block) || + convert_in_user(&up->blocks, &kp->blocks) || get_user(tmp, &up->edid) || - copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved))) + put_user(compat_ptr(tmp), &kp->edid) || + copy_in_user(kp->reserved, up->reserved, sizeof(kp->reserved))) return -EFAULT; - kp->edid = (__force u8 *)compat_ptr(tmp); return 0; } -static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up) +static int put_v4l2_subdev_edid32(struct v4l2_subdev_edid __user *kp, struct v4l2_subdev_edid32 __user *up) { - u32 tmp = (u32)((unsigned long)kp->edid); - - if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_edid32)) || - put_user(kp->pad, &up->pad) || - put_user(kp->start_block, &up->start_block) || - put_user(kp->blocks, &up->blocks) || - put_user(tmp, &up->edid) || - copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved))) + void *edid; + + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_subdev_edid32)) || + convert_in_user(&kp->pad, &up->pad) || + convert_in_user(&kp->start_block, &up->start_block) || + convert_in_user(&kp->blocks, &up->blocks) || + get_user(edid, &kp->edid) || + put_user(ptr_to_compat(edid), &up->edid) || + copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved))) return -EFAULT; return 0; } @@ -860,26 +1004,40 @@ static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up) #define VIDIOC_G_OUTPUT32 _IOR ('V', 46, s32) #define VIDIOC_S_OUTPUT32 _IOWR('V', 47, s32) +/* + * Note that these macros contain return statements to avoid the need for the + * "caller" to check return values. + */ +#define ALLOC_USER_SPACE(size) \ +({ \ + void __user *up_native; \ + up_native = compat_alloc_user_space(size); \ + if (!up_native) \ + return -ENOMEM; \ + if (clear_user(up_native, size)) \ + return -EFAULT; \ + up_native; \ +}) + +#define ALLOC_AND_GET(bufsizefunc, getfunc, structname) \ + do { \ + aux_space = bufsizefunc(up); \ + if (aux_space < 0) \ + return aux_space; \ + up_native = ALLOC_USER_SPACE(sizeof(struct structname) + aux_space); \ + aux_buf = up_native + sizeof(struct structname); \ + err = getfunc(up_native, up, aux_buf, aux_space); \ + } while (0) + static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - union { - struct v4l2_format v2f; - struct v4l2_buffer v2b; - struct v4l2_framebuffer v2fb; - struct v4l2_input v2i; - struct v4l2_standard v2s; - struct v4l2_ext_controls v2ecs; - struct v4l2_event v2ev; - struct v4l2_create_buffers v2crt; - struct v4l2_edid v2edid; - unsigned long vx; - int vi; - } karg; void __user *up = compat_ptr(arg); + void __user *up_native = NULL; + void __user *aux_buf; + int aux_space; int compatible_arg = 1; long err = 0; - memset(&karg, 0, sizeof(karg)); /* First, convert the command. */ switch (cmd) { case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break; @@ -915,30 +1073,35 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar case VIDIOC_STREAMOFF: case VIDIOC_S_INPUT: case VIDIOC_S_OUTPUT: - err = get_user(karg.vi, (s32 __user *)up); + up_native = ALLOC_USER_SPACE(sizeof(unsigned __user)); + if (convert_in_user((compat_uint_t __user *)up, + (unsigned __user *) up_native)) + return -EFAULT; compatible_arg = 0; break; case VIDIOC_G_INPUT: case VIDIOC_G_OUTPUT: + up_native = ALLOC_USER_SPACE(sizeof(unsigned __user)); compatible_arg = 0; break; case VIDIOC_G_EDID: case VIDIOC_S_EDID: - err = get_v4l2_edid32(&karg.v2edid, up); + up_native = ALLOC_USER_SPACE(sizeof(struct v4l2_subdev_edid)); + err = get_v4l2_subdev_edid32(up_native, up); compatible_arg = 0; break; case VIDIOC_G_FMT: case VIDIOC_S_FMT: case VIDIOC_TRY_FMT: - err = get_v4l2_format32(&karg.v2f, up); + ALLOC_AND_GET(bufsize_v4l2_format32, get_v4l2_format32, v4l2_format); compatible_arg = 0; break; case VIDIOC_CREATE_BUFS: - err = get_v4l2_create32(&karg.v2crt, up); + ALLOC_AND_GET(bufsize_v4l2_create32, get_v4l2_create32, v4l2_create_buffers); compatible_arg = 0; break; @@ -946,36 +1109,41 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar case VIDIOC_QUERYBUF: case VIDIOC_QBUF: case VIDIOC_DQBUF: - err = get_v4l2_buffer32(&karg.v2b, up); + ALLOC_AND_GET(bufsize_v4l2_buffer32, get_v4l2_buffer32, v4l2_buffer); compatible_arg = 0; break; case VIDIOC_S_FBUF: - err = get_v4l2_framebuffer32(&karg.v2fb, up); + up_native = ALLOC_USER_SPACE(sizeof(struct v4l2_framebuffer)); + err = get_v4l2_framebuffer32(up_native, up); compatible_arg = 0; break; case VIDIOC_G_FBUF: + up_native = ALLOC_USER_SPACE(sizeof(struct v4l2_framebuffer)); compatible_arg = 0; break; case VIDIOC_ENUMSTD: - err = get_v4l2_standard32(&karg.v2s, up); + up_native = ALLOC_USER_SPACE(sizeof(struct v4l2_standard)); + err = get_v4l2_standard32(up_native, up); compatible_arg = 0; break; case VIDIOC_ENUMINPUT: - err = get_v4l2_input32(&karg.v2i, up); + up_native = ALLOC_USER_SPACE(sizeof(struct v4l2_input)); + err = get_v4l2_input32(up_native, up); compatible_arg = 0; break; case VIDIOC_G_EXT_CTRLS: case VIDIOC_S_EXT_CTRLS: case VIDIOC_TRY_EXT_CTRLS: - err = get_v4l2_ext_controls32(&karg.v2ecs, up); + ALLOC_AND_GET(bufsize_v4l2_ext_controls32, get_v4l2_ext_controls32, v4l2_ext_controls); compatible_arg = 0; break; case VIDIOC_DQEVENT: + up_native = ALLOC_USER_SPACE(sizeof(struct v4l2_event)); compatible_arg = 0; break; } @@ -984,13 +1152,8 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar if (compatible_arg) err = native_ioctl(file, cmd, (unsigned long)up); - else { - mm_segment_t old_fs = get_fs(); - - set_fs(KERNEL_DS); - err = native_ioctl(file, cmd, (unsigned long)&karg); - set_fs(old_fs); - } + else + err = native_ioctl(file, cmd, (unsigned long)up_native); /* Special case: even after an error we need to put the results back for these ioctls since the error_idx will @@ -999,7 +1162,7 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar case VIDIOC_G_EXT_CTRLS: case VIDIOC_S_EXT_CTRLS: case VIDIOC_TRY_EXT_CTRLS: - if (put_v4l2_ext_controls32(&karg.v2ecs, up)) + if (put_v4l2_ext_controls32(up_native, up)) err = -EFAULT; break; } @@ -1011,44 +1174,44 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar case VIDIOC_S_OUTPUT: case VIDIOC_G_INPUT: case VIDIOC_G_OUTPUT: - err = put_user(((s32)karg.vi), (s32 __user *)up); + err = convert_in_user(((unsigned __user *)up_native), + (compat_uint_t __user *)up); break; - case VIDIOC_G_FBUF: - err = put_v4l2_framebuffer32(&karg.v2fb, up); + err = put_v4l2_framebuffer32(up_native, up); break; case VIDIOC_DQEVENT: - err = put_v4l2_event32(&karg.v2ev, up); + err = put_v4l2_event32(up_native, up); break; case VIDIOC_G_EDID: case VIDIOC_S_EDID: - err = put_v4l2_edid32(&karg.v2edid, up); + err = put_v4l2_subdev_edid32(up_native, up); break; case VIDIOC_G_FMT: case VIDIOC_S_FMT: case VIDIOC_TRY_FMT: - err = put_v4l2_format32(&karg.v2f, up); + err = put_v4l2_format32(up_native, up); break; case VIDIOC_CREATE_BUFS: - err = put_v4l2_create32(&karg.v2crt, up); + err = put_v4l2_create32(up_native, up); break; case VIDIOC_QUERYBUF: case VIDIOC_QBUF: case VIDIOC_DQBUF: - err = put_v4l2_buffer32(&karg.v2b, up); + err = put_v4l2_buffer32(up_native, up); break; case VIDIOC_ENUMSTD: - err = put_v4l2_standard32(&karg.v2s, up); + err = put_v4l2_standard32(up_native, up); break; case VIDIOC_ENUMINPUT: - err = put_v4l2_input32(&karg.v2i, up); + err = put_v4l2_input32(up_native, up); break; } return err; diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c index 3bdea9771ddc..84ed97e4d2db 100755 --- a/drivers/misc/qseecom.c +++ b/drivers/misc/qseecom.c @@ -1,6 +1,6 @@ /*Qualcomm Secure Execution Environment Communicator (QSEECOM) driver * - * 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 @@ -8778,6 +8778,7 @@ static int qseecom_probe(struct platform_device *pdev) static int qseecom_remove(struct platform_device *pdev) { struct qseecom_registered_kclient_list *kclient = NULL; + struct qseecom_registered_kclient_list *kclient_tmp = NULL; unsigned long flags = 0; int ret = 0; int i; @@ -8787,10 +8788,8 @@ static int qseecom_remove(struct platform_device *pdev) atomic_set(&qseecom.qseecom_state, QSEECOM_STATE_NOT_READY); spin_lock_irqsave(&qseecom.registered_kclient_list_lock, flags); - list_for_each_entry(kclient, &qseecom.registered_kclient_list_head, - list) { - if (!kclient) - goto exit_irqrestore; + list_for_each_entry_safe(kclient, kclient_tmp, + &qseecom.registered_kclient_list_head, list) { /* Break the loop if client handle is NULL */ if (!kclient->handle) @@ -8814,7 +8813,7 @@ static int qseecom_remove(struct platform_device *pdev) kzfree(kclient->handle); exit_free_kclient: kzfree(kclient); -exit_irqrestore: + spin_unlock_irqrestore(&qseecom.registered_kclient_list_lock, flags); if (qseecom.qseos_version > QSEEE_VERSION_00) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index f670b193d61f..a8a688e91a6e 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -466,10 +466,11 @@ int mmc_clk_update_freq(struct mmc_host *host, } EXPORT_SYMBOL(mmc_clk_update_freq); -void mmc_recovery_fallback_lower_speed(struct mmc_host *host) +int mmc_recovery_fallback_lower_speed(struct mmc_host *host) { + int err = 0; if (!host->card) - return; + return -EINVAL; if (host->sdr104_wa && mmc_card_sd(host->card) && (host->ios.timing == MMC_TIMING_UHS_SDR104) && @@ -477,9 +478,17 @@ void mmc_recovery_fallback_lower_speed(struct mmc_host *host) pr_err("%s: %s: blocked SDR104, lower the bus-speed (SDR50 / DDR50)\n", mmc_hostname(host), __func__); mmc_host_clear_sdr104(host); - mmc_hw_reset(host); + err = mmc_hw_reset(host); host->card->sdr104_blocked = true; + } else { + /* If sdr104_wa is not present, just return status */ + err = host->bus_ops->alive(host); } + if (err) + pr_err("%s: %s: Fallback to lower speed mode failed with err=%d\n", + mmc_hostname(host), __func__, err); + + return err; } static int mmc_devfreq_set_target(struct device *dev, @@ -547,7 +556,7 @@ static int mmc_devfreq_set_target(struct device *dev, if (err && err != -EAGAIN) { pr_err("%s: clock scale to %lu failed with error %d\n", mmc_hostname(host), *freq, err); - mmc_recovery_fallback_lower_speed(host); + err = mmc_recovery_fallback_lower_speed(host); } else { pr_debug("%s: clock change to %lu finished successfully (%s)\n", mmc_hostname(host), *freq, current->comm); @@ -2962,8 +2971,16 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr) */ mmc_host_clk_hold(host); err = mmc_wait_for_cmd(host, &cmd, 0); - if (err) - goto err_command; + if (err) { + if (err == -ETIMEDOUT) { + pr_debug("%s: voltage switching failed with err %d\n", + mmc_hostname(host), err); + err = -EAGAIN; + goto power_cycle; + } else { + goto err_command; + } + } if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR)) { err = -EIO; @@ -4135,8 +4152,7 @@ int _mmc_detect_card_removed(struct mmc_host *host) if (ret) { if (host->ops->get_cd && host->ops->get_cd(host)) { - mmc_recovery_fallback_lower_speed(host); - ret = 0; + ret = mmc_recovery_fallback_lower_speed(host); } else { mmc_card_set_removed(host->card); if (host->card->sdr104_blocked) { diff --git a/drivers/mmc/host/cmdq_hci.c b/drivers/mmc/host/cmdq_hci.c index 83d24fcaf2ab..54c86cb6d0fc 100644 --- a/drivers/mmc/host/cmdq_hci.c +++ b/drivers/mmc/host/cmdq_hci.c @@ -357,7 +357,7 @@ static int cmdq_host_alloc_tdl(struct cmdq_host *cq_host) if (!cq_host->desc_base || !cq_host->trans_desc_base) return -ENOMEM; - pr_info("desc-base: 0x%p trans-base: 0x%p\n desc_dma 0x%llx trans_dma: 0x%llx\n", + pr_debug("desc-base: 0x%pK trans-base: 0x%pK\n desc_dma 0x%llx trans_dma: 0x%llx\n", cq_host->desc_base, cq_host->trans_desc_base, (unsigned long long)cq_host->desc_dma_base, (unsigned long long) cq_host->trans_desc_dma_base); diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 4b4923100504..1adeb712c548 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. * Copyright (C) 2018 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify @@ -3947,11 +3947,10 @@ void sdhci_msm_pm_qos_cpu_init(struct sdhci_host *host, group->latency = PM_QOS_DEFAULT_VALUE; pm_qos_add_request(&group->req, PM_QOS_CPU_DMA_LATENCY, group->latency); - pr_info("%s (): voted for group #%d (mask=0x%lx) latency=%d (0x%p)\n", + pr_info("%s (): voted for group #%d (mask=0x%lx) latency=%d\n", __func__, i, group->req.cpus_affine.bits[0], - group->latency, - &latency[i].latency[SDHCI_PERFORMANCE_MODE]); + group->latency); } msm_host->pm_qos_prev_cpu = -1; msm_host->pm_qos_group_enable = true; @@ -4503,8 +4502,6 @@ static int sdhci_msm_probe(struct platform_device *pdev) goto vreg_deinit; } writel_relaxed(readl_relaxed(tlmm_mem) | 0x2, tlmm_mem); - dev_dbg(&pdev->dev, "tlmm reg %pa value 0x%08x\n", - &tlmm_memres->start, readl_relaxed(tlmm_mem)); } /* diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 5906bba0aeff..dc896ba5796b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2993,13 +2993,13 @@ static void sdhci_adma_show_error(struct sdhci_host *host) struct sdhci_adma2_64_desc *dma_desc = desc; if (host->flags & SDHCI_USE_64_BIT_DMA) - DBG("%s: %p: DMA 0x%08x%08x, LEN 0x%04x, Attr=0x%02x\n", + DBG("%s: %pK: DMA 0x%08x%08x, LEN 0x%04x,Attr=0x%02x\n", name, desc, le32_to_cpu(dma_desc->addr_hi), le32_to_cpu(dma_desc->addr_lo), le16_to_cpu(dma_desc->len), le16_to_cpu(dma_desc->cmd)); else - DBG("%s: %p: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n", + DBG("%s: %pK: DMA 0x%08x, LEN 0x%04x, Attr=0x%02x\n", name, desc, le32_to_cpu(dma_desc->addr_lo), le16_to_cpu(dma_desc->len), le16_to_cpu(dma_desc->cmd)); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 2fa54b0b0679..7948c5a9e5fc 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -100,9 +100,9 @@ static void unfill_desc(struct hnae_ring *ring) ring_ptr_move_bw(ring, next_to_use); } -int hns_nic_net_xmit_hw(struct net_device *ndev, - struct sk_buff *skb, - struct hns_nic_ring_data *ring_data) +netdev_tx_t hns_nic_net_xmit_hw(struct net_device *ndev, + struct sk_buff *skb, + struct hns_nic_ring_data *ring_data) { struct hns_nic_priv *priv = netdev_priv(ndev); struct hnae_ring *ring = ring_data->ring; @@ -172,6 +172,10 @@ int hns_nic_net_xmit_hw(struct net_device *ndev, dev_queue = netdev_get_tx_queue(ndev, skb->queue_mapping); netdev_tx_sent_queue(dev_queue, skb->len); + ndev->trans_start = jiffies; + ndev->stats.tx_bytes += skb->len; + ndev->stats.tx_packets++; + wmb(); /* commit all data before submit */ assert(skb->queue_mapping < priv->ae_handle->q_num); hnae_queue_xmit(priv->ae_handle->qs[skb->queue_mapping], buf_num); @@ -1089,17 +1093,11 @@ static netdev_tx_t hns_nic_net_xmit(struct sk_buff *skb, struct net_device *ndev) { struct hns_nic_priv *priv = netdev_priv(ndev); - int ret; assert(skb->queue_mapping < ndev->ae_handle->q_num); - ret = hns_nic_net_xmit_hw(ndev, skb, - &tx_ring_data(priv, skb->queue_mapping)); - if (ret == NETDEV_TX_OK) { - ndev->trans_start = jiffies; - ndev->stats.tx_bytes += skb->len; - ndev->stats.tx_packets++; - } - return (netdev_tx_t)ret; + + return hns_nic_net_xmit_hw(ndev, skb, + &tx_ring_data(priv, skb->queue_mapping)); } static int hns_nic_change_mtu(struct net_device *ndev, int new_mtu) diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.h b/drivers/net/ethernet/hisilicon/hns/hns_enet.h index dae0ed19ac6d..b4489ffdb4de 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.h @@ -77,8 +77,8 @@ void hns_ethtool_set_ops(struct net_device *ndev); void hns_nic_net_reset(struct net_device *ndev); void hns_nic_net_reinit(struct net_device *netdev); int hns_nic_init_phy(struct net_device *ndev, struct hnae_handle *h); -int hns_nic_net_xmit_hw(struct net_device *ndev, - struct sk_buff *skb, - struct hns_nic_ring_data *ring_data); +netdev_tx_t hns_nic_net_xmit_hw(struct net_device *ndev, + struct sk_buff *skb, + struct hns_nic_ring_data *ring_data); #endif /**__HNS_ENET_H */ diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index c2ea4e5666fb..c0bcfa979253 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include @@ -1964,143 +1963,6 @@ static int __usbnet_write_cmd(struct usbnet *dev, u8 cmd, u8 reqtype, return err; } -int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr, - struct usb_interface *intf, - u8 *buffer, - int buflen) -{ - /* duplicates are ignored */ - struct usb_cdc_union_desc *union_header = NULL; - - /* duplicates are not tolerated */ - struct usb_cdc_header_desc *header = NULL; - struct usb_cdc_ether_desc *ether = NULL; - struct usb_cdc_mdlm_detail_desc *detail = NULL; - struct usb_cdc_mdlm_desc *desc = NULL; - - unsigned int elength; - int cnt = 0; - - memset(hdr, 0x00, sizeof(struct usb_cdc_parsed_header)); - hdr->phonet_magic_present = false; - while (buflen > 0) { - elength = buffer[0]; - if (!elength) { - dev_err(&intf->dev, "skipping garbage byte\n"); - elength = 1; - goto next_desc; - } - if (buffer[1] != USB_DT_CS_INTERFACE) { - dev_err(&intf->dev, "skipping garbage\n"); - goto next_desc; - } - - switch (buffer[2]) { - case USB_CDC_UNION_TYPE: /* we've found it */ - if (elength < sizeof(struct usb_cdc_union_desc)) - goto next_desc; - if (union_header) { - dev_err(&intf->dev, "More than one union descriptor, skipping ...\n"); - goto next_desc; - } - union_header = (struct usb_cdc_union_desc *)buffer; - break; - case USB_CDC_COUNTRY_TYPE: - if (elength < sizeof(struct usb_cdc_country_functional_desc)) - goto next_desc; - hdr->usb_cdc_country_functional_desc = - (struct usb_cdc_country_functional_desc *)buffer; - break; - case USB_CDC_HEADER_TYPE: - if (elength != sizeof(struct usb_cdc_header_desc)) - goto next_desc; - if (header) - return -EINVAL; - header = (struct usb_cdc_header_desc *)buffer; - break; - case USB_CDC_ACM_TYPE: - if (elength < sizeof(struct usb_cdc_acm_descriptor)) - goto next_desc; - hdr->usb_cdc_acm_descriptor = - (struct usb_cdc_acm_descriptor *)buffer; - break; - case USB_CDC_ETHERNET_TYPE: - if (elength != sizeof(struct usb_cdc_ether_desc)) - goto next_desc; - if (ether) - return -EINVAL; - ether = (struct usb_cdc_ether_desc *)buffer; - break; - case USB_CDC_CALL_MANAGEMENT_TYPE: - if (elength < sizeof(struct usb_cdc_call_mgmt_descriptor)) - goto next_desc; - hdr->usb_cdc_call_mgmt_descriptor = - (struct usb_cdc_call_mgmt_descriptor *)buffer; - break; - case USB_CDC_DMM_TYPE: - if (elength < sizeof(struct usb_cdc_dmm_desc)) - goto next_desc; - hdr->usb_cdc_dmm_desc = - (struct usb_cdc_dmm_desc *)buffer; - break; - case USB_CDC_MDLM_TYPE: - if (elength < sizeof(struct usb_cdc_mdlm_desc *)) - goto next_desc; - if (desc) - return -EINVAL; - desc = (struct usb_cdc_mdlm_desc *)buffer; - break; - case USB_CDC_MDLM_DETAIL_TYPE: - if (elength < sizeof(struct usb_cdc_mdlm_detail_desc *)) - goto next_desc; - if (detail) - return -EINVAL; - detail = (struct usb_cdc_mdlm_detail_desc *)buffer; - break; - case USB_CDC_NCM_TYPE: - if (elength < sizeof(struct usb_cdc_ncm_desc)) - goto next_desc; - hdr->usb_cdc_ncm_desc = (struct usb_cdc_ncm_desc *)buffer; - break; - case USB_CDC_MBIM_TYPE: - if (elength < sizeof(struct usb_cdc_mbim_desc)) - goto next_desc; - - hdr->usb_cdc_mbim_desc = (struct usb_cdc_mbim_desc *)buffer; - break; - case USB_CDC_MBIM_EXTENDED_TYPE: - if (elength < sizeof(struct usb_cdc_mbim_extended_desc)) - break; - hdr->usb_cdc_mbim_extended_desc = - (struct usb_cdc_mbim_extended_desc *)buffer; - break; - case CDC_PHONET_MAGIC_NUMBER: - hdr->phonet_magic_present = true; - break; - default: - /* - * there are LOTS more CDC descriptors that - * could legitimately be found here. - */ - dev_dbg(&intf->dev, "Ignoring descriptor: type %02x, length %ud\n", - buffer[2], elength); - goto next_desc; - } - cnt++; -next_desc: - buflen -= elength; - buffer += elength; - } - hdr->usb_cdc_union_desc = union_header; - hdr->usb_cdc_header_desc = header; - hdr->usb_cdc_mdlm_detail_desc = detail; - hdr->usb_cdc_mdlm_desc = desc; - hdr->usb_cdc_ether_desc = ether; - return cnt; -} - -EXPORT_SYMBOL(cdc_parse_cdc_header); - /* * The function can't be called inside suspend/resume callback, * otherwise deadlock will be caused. diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index 769f89e8d14c..9ccd6212b54a 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -96,13 +96,29 @@ static inline u32 ath10k_ce_src_ring_write_index_get(struct ath10k *ar, ce_ctrl_addr + ar->hw_ce_regs->sr_wr_index_addr); } +static inline u32 ath10k_ce_src_ring_read_index_get_from_ddr( + struct ath10k *ar, u32 ce_id) +{ + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + + return ar_opaque->vaddr_rri_on_ddr[ce_id] & CE_DDR_RRI_MASK; +} + static inline u32 ath10k_ce_src_ring_read_index_get(struct ath10k *ar, u32 ce_ctrl_addr) { struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + u32 ce_id = COPY_ENGINE_ID(ce_ctrl_addr); + struct ath10k_ce_pipe *ce_state = &ar_opaque->ce_states[ce_id]; + u32 index; - return ar_opaque->bus_ops->read32(ar, + if (ar->rri_on_ddr && (ce_state->attr_flags & CE_ATTR_DIS_INTR)) + index = ath10k_ce_src_ring_read_index_get_from_ddr(ar, ce_id); + else + index = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr + ar->hw_ce_regs->current_srri_addr); + + return index; } static inline void ath10k_ce_shadow_src_ring_write_index_set(struct ath10k *ar, @@ -195,9 +211,19 @@ static inline u32 ath10k_ce_dest_ring_read_index_get(struct ath10k *ar, u32 ce_ctrl_addr) { struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + u32 ce_id = COPY_ENGINE_ID(ce_ctrl_addr); + struct ath10k_ce_pipe *ce_state = &ar_opaque->ce_states[ce_id]; + u32 index; - return ar_opaque->bus_ops->read32(ar, + if (ar->rri_on_ddr && (ce_state->attr_flags & CE_ATTR_DIS_INTR)) + index = (ar_opaque->vaddr_rri_on_ddr[ce_id] >> + CE_DDR_RRI_SHIFT) & + CE_DDR_RRI_MASK; + else + index = ar_opaque->bus_ops->read32(ar, ce_ctrl_addr + ar->hw_ce_regs->current_drri_addr); + + return index; } static inline void ath10k_ce_dest_ring_base_addr_set(struct ath10k *ar, @@ -449,7 +475,7 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, struct ath10k_ce_ring *src_ring = ce_state->src_ring; struct ce_desc *desc, sdesc; unsigned int nentries_mask = src_ring->nentries_mask; - unsigned int sw_index = src_ring->sw_index; + unsigned int sw_index; unsigned int write_index = src_ring->write_index; u32 ctrl_addr = ce_state->ctrl_addr; u32 desc_flags = 0; @@ -462,6 +488,7 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, ath10k_warn(ar, "%s: send more we can (nbytes: %d, max: %d)\n", __func__, nbytes, ce_state->src_sz_max); + sw_index = ath10k_ce_src_ring_read_index_get_from_ddr(ar, ce_state->id); if (unlikely(CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) <= 0)) { ret = -ENOSR; @@ -1235,6 +1262,52 @@ ath10k_ce_alloc_dest_ring(struct ath10k *ar, unsigned int ce_id, return dest_ring; } +void ce_config_rri_on_ddr(struct ath10k *ar) +{ + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + u32 hi_paddr, low_paddr; + u32 ce_base_addr; + u32 ctrl1_regs; + int i; + + ar_opaque->vaddr_rri_on_ddr = + (u32 *)dma_alloc_coherent(ar->dev, + (CE_COUNT * sizeof(u32)), + &ar_opaque->paddr_rri_on_ddr, GFP_KERNEL); + + if (!ar_opaque->vaddr_rri_on_ddr) + return; + + low_paddr = lower_32_bits(ar_opaque->paddr_rri_on_ddr); + hi_paddr = upper_32_bits(ar_opaque->paddr_rri_on_ddr) & + CE_DESC_FLAGS_GET_MASK; + + ar_opaque->bus_ops->write32(ar, ar->hw_ce_regs->ce_rri_low, low_paddr); + ar_opaque->bus_ops->write32(ar, ar->hw_ce_regs->ce_rri_high, hi_paddr); + + for (i = 0; i < CE_COUNT; i++) { + ctrl1_regs = ar->hw_ce_regs->ctrl1_regs->addr; + ce_base_addr = ath10k_ce_base_address(ar, i); + ar_opaque->bus_ops->write32(ar, ce_base_addr + ctrl1_regs, + ar_opaque->bus_ops->read32(ar, ce_base_addr + ctrl1_regs) | + ar->hw_ce_regs->upd->mask); + } + + memset(ar_opaque->vaddr_rri_on_ddr, 0, CE_COUNT * sizeof(u32)); +} + +void ce_remove_rri_on_ddr(struct ath10k *ar) +{ + struct bus_opaque *ar_opaque = ath10k_bus_priv(ar); + + if (!ar_opaque->vaddr_rri_on_ddr) + return; + + dma_free_coherent(ar->dev, (CE_COUNT * sizeof(u32)), + ar_opaque->vaddr_rri_on_ddr, + ar_opaque->paddr_rri_on_ddr); +} + /* * Initialize a Copy Engine based on caller-supplied attributes. * This may be called once to initialize both source and destination diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h index 936f0698c0f0..fe5f5680ca3d 100644 --- a/drivers/net/wireless/ath/ath10k/ce.h +++ b/drivers/net/wireless/ath/ath10k/ce.h @@ -42,6 +42,8 @@ struct ath10k_ce_pipe; #define CE_DESC_FLAGS_GET_MASK 0x1F #define CE_DESC_37BIT_ADDR_MASK 0x1FFFFFFFFF +#define CE_DDR_RRI_MASK 0xFFFF +#define CE_DDR_RRI_SHIFT 16 /* Following desc flags are used in QCA99X0 */ #define CE_DESC_FLAGS_HOST_INT_DIS (1 << 2) @@ -211,6 +213,8 @@ struct bus_opaque { spinlock_t ce_lock; const struct ath10k_bus_ops *bus_ops; struct ath10k_ce_pipe ce_states[CE_COUNT_MAX]; + u32 *vaddr_rri_on_ddr; + dma_addr_t paddr_rri_on_ddr; }; /*==================Send====================*/ @@ -288,6 +292,8 @@ void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id); int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, const struct ce_attr *attr); void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id); +void ce_config_rri_on_ddr(struct ath10k *ar); +void ce_remove_rri_on_ddr(struct ath10k *ar); /*==================CE Engine Shutdown=======================*/ /* diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 4ed144d48e2b..00781772a175 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1300,13 +1300,15 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar) int ret; struct ath10k_fw_file *fw_file; - if (!ar->is_bmi && QCA_REV_WCN3990(ar)) { + if (!ar->is_bmi) { fw_file = &ar->normal_mode_fw.fw_file; fw_file->wmi_op_version = ATH10K_FW_WMI_OP_VERSION_TLV; fw_file->htt_op_version = ATH10K_FW_HTT_OP_VERSION_TLV; __set_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, fw_file->fw_features); __set_bit(WMI_SERVICE_WOW, ar->wmi.svc_map); + __set_bit(ATH10K_FW_FEATURE_NO_NWIFI_DECAP_4ADDR_PADDING, + fw_file->fw_features); return 0; } @@ -1710,6 +1712,7 @@ static int ath10k_core_init_firmware_features(struct ath10k *ar) ar->fw_stats_req_mask = WMI_STAT_PDEV | WMI_STAT_VDEV | WMI_STAT_PEER; ar->max_spatial_stream = WMI_MAX_SPATIAL_STREAM; + ar->wmi.mgmt_max_num_pending_tx = TARGET_TLV_MGMT_NUM_MSDU_DESC; break; case ATH10K_FW_WMI_OP_VERSION_10_4: ar->max_num_peers = TARGET_10_4_NUM_PEERS; @@ -2371,6 +2374,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, ar->fw_flags = &wcn3990_fw_flags; ar->shadow_reg_value = &wcn3990_shadow_reg_value; ar->shadow_reg_address = &wcn3990_shadow_reg_address; + ar->rri_on_ddr = true; break; default: ath10k_err(ar, "unsupported core hardware revision %d\n", diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 64ecd8f4e054..233aaa563b59 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -174,6 +174,10 @@ struct ath10k_wmi { const struct wmi_ops *ops; const struct wmi_peer_flags_map *peer_flags; + u32 mgmt_max_num_pending_tx; + struct idr mgmt_pending_tx; + /* Protects access to mgmt_pending_tx, mgmt_max_num_pending_tx */ + spinlock_t mgmt_tx_lock; u32 num_mem_chunks; u32 rx_decap_mode; struct ath10k_mem_chunk mem_chunks[WMI_MAX_MEM_REQS]; @@ -967,6 +971,7 @@ struct ath10k { struct completion peer_delete_done; bool is_bmi; enum ieee80211_sta_state sta_state; + bool rri_on_ddr; /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); }; diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c index 1437b5d29a17..558214cef688 100644 --- a/drivers/net/wireless/ath/ath10k/hw.c +++ b/drivers/net/wireless/ath/ath10k/hw.c @@ -284,6 +284,12 @@ struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_dst_ring = { .wm_high = &wcn3990_dst_wm_high, }; +static struct ath10k_hw_ce_ctrl1_upd wcn3990_ctrl1_upd = { + .shift = 19, + .mask = 0x00080000, + .enable = 0x00000000, +}; + struct ath10k_hw_ce_regs wcn3990_ce_regs = { .sr_base_addr = 0x00000000, .sr_size_addr = 0x00000008, @@ -305,6 +311,7 @@ struct ath10k_hw_ce_regs wcn3990_ce_regs = { .misc_regs = &wcn3990_misc_reg, .wm_srcr = &wcn3990_wm_src_ring, .wm_dstr = &wcn3990_wm_dst_ring, + .upd = &wcn3990_ctrl1_upd, }; struct ath10k_hw_ce_regs_addr_map qcax_src_ring = { diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index a37b956c558f..2c5f2ba6322c 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -332,6 +332,12 @@ struct ath10k_hw_ce_dst_src_wm_regs { struct ath10k_hw_ce_regs_addr_map *wm_high; }; +struct ath10k_hw_ce_ctrl1_upd { + u32 shift; + u32 mask; + u32 enable; +}; + struct ath10k_hw_ce_regs { u32 sr_base_addr; u32 sr_size_addr; @@ -355,6 +361,7 @@ struct ath10k_hw_ce_regs { struct ath10k_hw_ce_host_ie *host_ie; struct ath10k_hw_ce_dst_src_wm_regs *wm_srcr; struct ath10k_hw_ce_dst_src_wm_regs *wm_dstr; + struct ath10k_hw_ce_ctrl1_upd *upd; }; extern struct ath10k_hw_ce_regs wcn3990_ce_regs; @@ -625,6 +632,8 @@ ath10k_rx_desc_get_l3_pad_bytes(struct ath10k_hw_params *hw, #define TARGET_TLV_NUM_TIDS ((TARGET_TLV_NUM_PEERS) * 2) #define TARGET_TLV_NUM_MSDU_DESC (1024 + 32) #define TARGET_TLV_NUM_WOW_PATTERNS 22 +/* FW supports max 50 outstanding mgmt cmds */ +#define TARGET_TLV_MGMT_NUM_MSDU_DESC (50) /* Target specific defines for WMI-HL-1.0 firmware */ #define TARGET_HL_10_TLV_NUM_PEERS 14 diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 894ce345ee16..e69610365d6e 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3704,6 +3704,7 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work) { struct ath10k *ar = container_of(work, struct ath10k, wmi_mgmt_tx_work); struct sk_buff *skb; + dma_addr_t paddr; int ret; for (;;) { @@ -3711,11 +3712,26 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work) if (!skb) break; - ret = ath10k_wmi_mgmt_tx(ar, skb); - if (ret) { - ath10k_warn(ar, "failed to transmit management frame via WMI: %d\n", - ret); - ieee80211_free_txskb(ar->hw, skb); + if (QCA_REV_WCN3990(ar)) { + paddr = dma_map_single(ar->dev, skb->data, + skb->len, DMA_TO_DEVICE); + if (!paddr) + continue; + ret = ath10k_wmi_mgmt_tx_send(ar, skb, paddr); + if (ret) { + ath10k_warn(ar, "failed to transmit management frame by ref via WMI: %d\n", + ret); + dma_unmap_single(ar->dev, paddr, skb->len, + DMA_FROM_DEVICE); + ieee80211_free_txskb(ar->hw, skb); + } + } else { + ret = ath10k_wmi_mgmt_tx(ar, skb); + if (ret) { + ath10k_warn(ar, "failed to transmit management frame via WMI: %d\n", + ret); + ieee80211_free_txskb(ar->hw, skb); + } } } } @@ -4515,6 +4531,13 @@ static int ath10k_start(struct ieee80211_hw *hw) goto err_core_stop; } + param = ar->wmi.pdev_param->idle_ps_config; + ret = ath10k_wmi_pdev_set_param(ar, param, 1); + if (ret) { + ath10k_warn(ar, "failed to enable idle_ps_config: %d\n", ret); + goto err_core_stop; + } + if (test_bit(WMI_SERVICE_ADAPTIVE_OCS, ar->wmi.svc_map)) { ret = ath10k_wmi_adaptive_qcs(ar, true); if (ret) { @@ -4955,6 +4978,15 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, goto err; } + if ((arvif->vdev_type == WMI_VDEV_TYPE_STA) && QCA_REV_WCN3990(ar)) { + ret = ath10k_wmi_csa_offload(ar, arvif->vdev_id, true); + if (ret) { + ath10k_err(ar, "CSA offload failed for vdev %i: %d\n", + arvif->vdev_id, ret); + goto err_vdev_delete; + } + } + ar->free_vdev_map &= ~(1LL << arvif->vdev_id); list_add(&arvif->list, &ar->arvifs); @@ -5167,6 +5199,9 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, kfree(arvif->u.ap.noa_data); } + if ((arvif->vdev_type == WMI_VDEV_TYPE_STA) && QCA_REV_WCN3990(ar)) + ath10k_wmi_csa_offload(ar, arvif->vdev_id, false); + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %i delete (remove interface)\n", arvif->vdev_id); @@ -5618,6 +5653,22 @@ static void ath10k_set_key_h_def_keyidx(struct ath10k *ar, arvif->vdev_id, ret); } +static void ath10k_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + + mutex_lock(&ar->conf_mutex); + memcpy(&arvif->gtk_rekey_data.kek, data->kek, NL80211_KEK_LEN); + memcpy(&arvif->gtk_rekey_data.kck, data->kck, NL80211_KCK_LEN); + arvif->gtk_rekey_data.replay_ctr = + be64_to_cpup((__be64 *)data->replay_ctr); + arvif->gtk_rekey_data.valid = true; + mutex_unlock(&ar->conf_mutex); +} + static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) @@ -7545,6 +7596,7 @@ static const struct ieee80211_ops ath10k_ops = { .bss_info_changed = ath10k_bss_info_changed, .hw_scan = ath10k_hw_scan, .cancel_hw_scan = ath10k_cancel_hw_scan, + .set_rekey_data = ath10k_set_rekey_data, .set_key = ath10k_set_key, .set_default_unicast_key = ath10k_set_default_unicast_key, .sta_state = ath10k_sta_state, @@ -7580,7 +7632,6 @@ static const struct ieee80211_ops ath10k_ops = { .suspend = ath10k_wow_op_suspend, .resume = ath10k_wow_op_resume, .set_wakeup = ath10k_wow_op_set_wakeup, - .set_rekey_data = ath10k_wow_op_set_rekey_data, #endif #ifdef CONFIG_MAC80211_DEBUGFS .sta_add_debugfs = ath10k_sta_add_debugfs, @@ -8254,6 +8305,7 @@ int ath10k_mac_register(struct ath10k *ar) void ath10k_mac_unregister(struct ath10k *ar) { + ath10k_wow_deinit(ar); ieee80211_unregister_hw(ar->hw); if (IS_ENABLED(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector) diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index da5946941410..1317d184e1e8 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -1,6 +1,6 @@ /* Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. - * 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 @@ -646,10 +646,6 @@ static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id, struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); struct ath10k_snoc_pipe *snoc_pipe; struct ath10k_ce_pipe *ce_pipe; - struct ath10k_ce_ring *src_ring; - unsigned int nentries_mask; - unsigned int sw_index; - unsigned int write_index; int err, i = 0; if (!ar_snoc) @@ -660,19 +656,8 @@ static int ath10k_snoc_hif_tx_sg(struct ath10k *ar, u8 pipe_id, snoc_pipe = &ar_snoc->pipe_info[pipe_id]; ce_pipe = snoc_pipe->ce_hdl; - src_ring = ce_pipe->src_ring; spin_lock_bh(&ar_snoc->opaque_ctx.ce_lock); - nentries_mask = src_ring->nentries_mask; - sw_index = src_ring->sw_index; - write_index = src_ring->write_index; - - if (unlikely(CE_RING_DELTA(nentries_mask, - write_index, sw_index - 1) < n_items)) { - err = -ENOBUFS; - goto err; - } - for (i = 0; i < n_items - 1; i++) { ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc tx item %d paddr %pad len %d n_items %d\n", @@ -967,6 +952,8 @@ static void ath10k_snoc_hif_power_down(struct ath10k *ar) if (!atomic_read(&ar_snoc->pm_ops_inprogress)) ath10k_snoc_qmi_wlan_disable(ar); + + ce_remove_rri_on_ddr(ar); } int ath10k_snoc_get_ce_id(struct ath10k *ar, int irq) @@ -1076,6 +1063,7 @@ static int ath10k_snoc_get_soc_info(struct ath10k *ar) static int ath10k_snoc_wlan_enable(struct ath10k *ar) { struct ath10k_wlan_enable_cfg cfg; + enum ath10k_driver_mode mode; int pipe_num; struct ath10k_ce_tgt_pipe_cfg tgt_cfg[CE_COUNT_MAX]; @@ -1106,8 +1094,9 @@ static int ath10k_snoc_wlan_enable(struct ath10k *ar) cfg.shadow_reg_cfg = (struct ath10k_shadow_reg_cfg *) &target_shadow_reg_cfg_map; - return ath10k_snoc_qmi_wlan_enable(ar, &cfg, - ATH10K_MISSION, "5.1.0.26N"); + mode = ar->testmode.utf_monitor ? ATH10K_FTM : ATH10K_MISSION; + return ath10k_snoc_qmi_wlan_enable(ar, &cfg, mode, + "5.1.0.26N"); } static int ath10k_snoc_bus_configure(struct ath10k *ar) @@ -1121,6 +1110,8 @@ static int ath10k_snoc_bus_configure(struct ath10k *ar) return ret; } + ce_config_rri_on_ddr(ar); + return 0; } diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c index ed85f938e3c0..1a067a4ece4d 100644 --- a/drivers/net/wireless/ath/ath10k/testmode.c +++ b/drivers/net/wireless/ath/ath10k/testmode.c @@ -137,6 +137,13 @@ static int ath10k_tm_cmd_get_version(struct ath10k *ar, struct nlattr *tb[]) return ret; } + ret = nla_put_u32(skb, ATH10K_TM_ATTR_WMI_OP_VERSION, + ar->normal_mode_fw.fw_file.wmi_op_version); + if (ret) { + kfree_skb(skb); + return ret; + } + return cfg80211_testmode_reply(skb); } @@ -174,8 +181,15 @@ static int ath10k_tm_fetch_utf_firmware_api_1(struct ath10k *ar, static int ath10k_tm_fetch_firmware(struct ath10k *ar) { struct ath10k_fw_components *utf_mode_fw; + struct ath10k_fw_file *fw_file; int ret; + if (!ar->is_bmi) { + fw_file = &ar->testmode.utf_mode_fw.fw_file; + fw_file->wmi_op_version = ATH10K_FW_WMI_OP_VERSION_TLV; + fw_file->htt_op_version = ATH10K_FW_HTT_OP_VERSION_TLV; + return 0; + } ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_UTF_API2_FILE, &ar->testmode.utf_mode_fw.fw_file); if (ret == 0) { diff --git a/drivers/net/wireless/ath/ath10k/testmode_i.h b/drivers/net/wireless/ath/ath10k/testmode_i.h index ba81bf66ce85..191a8f34c8ea 100644 --- a/drivers/net/wireless/ath/ath10k/testmode_i.h +++ b/drivers/net/wireless/ath/ath10k/testmode_i.h @@ -33,6 +33,7 @@ enum ath10k_tm_attr { ATH10K_TM_ATTR_WMI_CMDID = 3, ATH10K_TM_ATTR_VERSION_MAJOR = 4, ATH10K_TM_ATTR_VERSION_MINOR = 5, + ATH10K_TM_ATTR_WMI_OP_VERSION = 6, /* keep last */ __ATH10K_TM_ATTR_AFTER_LAST, diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h index 98669342c530..745e4afad4dd 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-ops.h +++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h @@ -29,6 +29,8 @@ struct wmi_ops { struct wmi_scan_ev_arg *arg); int (*pull_mgmt_rx)(struct ath10k *ar, struct sk_buff *skb, struct wmi_mgmt_rx_ev_arg *arg); + int (*pull_mgmt_tx_compl)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_tlv_mgmt_tx_compl_ev_arg *arg); int (*pull_ch_info)(struct ath10k *ar, struct sk_buff *skb, struct wmi_ch_info_ev_arg *arg); int (*pull_peer_delete_resp)(struct ath10k *ar, struct sk_buff *skb, @@ -127,6 +129,9 @@ struct wmi_ops { enum wmi_force_fw_hang_type type, u32 delay_ms); struct sk_buff *(*gen_mgmt_tx)(struct ath10k *ar, struct sk_buff *skb); + struct sk_buff *(*gen_mgmt_tx_send)(struct ath10k *ar, + struct sk_buff *skb, + dma_addr_t paddr); struct sk_buff *(*gen_dbglog_cfg)(struct ath10k *ar, u64 module_enable, u32 log_level); struct sk_buff *(*gen_pktlog_enable)(struct ath10k *ar, u32 filter); @@ -206,6 +211,8 @@ struct wmi_ops { (struct ath10k *ar, enum wmi_bss_survey_req_type type); struct sk_buff *(*gen_echo)(struct ath10k *ar, u32 value); + struct sk_buff *(*gen_csa_offload)(struct ath10k *ar, + u32 vdev_id, bool enable); }; int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id); @@ -241,6 +248,16 @@ ath10k_wmi_pull_scan(struct ath10k *ar, struct sk_buff *skb, return ar->wmi.ops->pull_scan(ar, skb, arg); } +static inline int +ath10k_wmi_pull_mgmt_tx_compl(struct ath10k *ar, struct sk_buff *skb, + struct wmi_tlv_mgmt_tx_compl_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_mgmt_tx_compl) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_mgmt_tx_compl(ar, skb, arg); +} + static inline int ath10k_wmi_pull_mgmt_rx(struct ath10k *ar, struct sk_buff *skb, struct wmi_mgmt_rx_ev_arg *arg) @@ -390,13 +407,34 @@ ath10k_wmi_get_txbf_conf_scheme(struct ath10k *ar) return ar->wmi.ops->get_txbf_conf_scheme(ar); } +static inline int +ath10k_wmi_mgmt_tx_send(struct ath10k *ar, struct sk_buff *msdu, + dma_addr_t paddr) +{ + struct sk_buff *skb; + int ret; + + if (!ar->wmi.ops->gen_mgmt_tx_send) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_mgmt_tx_send(ar, msdu, paddr); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + ret = ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->mgmt_tx_send_cmdid); + if (ret) + return ret; + + return 0; +} + static inline int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(msdu); struct sk_buff *skb; int ret; - u32 mgmt_tx_cmdid; if (!ar->wmi.ops->gen_mgmt_tx) return -EOPNOTSUPP; @@ -405,12 +443,8 @@ ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu) if (IS_ERR(skb)) return PTR_ERR(skb); - if (QCA_REV_WCN3990(ar)) - mgmt_tx_cmdid = ar->wmi.cmd->mgmt_tx_send_cmdid; - else - mgmt_tx_cmdid = ar->wmi.cmd->mgmt_tx_cmdid; - - ret = ath10k_wmi_cmd_send(ar, skb, mgmt_tx_cmdid); + ret = ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->mgmt_tx_cmdid); if (ret) return ret; @@ -1454,6 +1488,23 @@ ath10k_wmi_pdev_bss_chan_info_request(struct ath10k *ar, wmi->cmd->pdev_bss_chan_info_request_cmdid); } +static inline int +ath10k_wmi_csa_offload(struct ath10k *ar, u32 vdev_id, bool enable) +{ + struct sk_buff *skb; + u32 cmd_id; + + if (!ar->wmi.ops->gen_csa_offload) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_csa_offload(ar, vdev_id, enable); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + cmd_id = ar->wmi.cmd->csa_offload_enable_cmdid; + return ath10k_wmi_cmd_send(ar, skb, cmd_id); +} + static inline int ath10k_wmi_echo(struct ath10k *ar, u32 value) { diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index 5ce4fdfca724..e48a20b59c93 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -558,6 +558,9 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb) case WMI_TLV_PEER_DELETE_RESP_EVENTID: ath10k_wmi_tlv_event_peer_delete_resp(ar, skb); break; + case WMI_TLV_MGMT_TX_COMPLETION_EVENTID: + ath10k_wmi_tlv_event_mgmt_tx_compl(ar, skb); + break; default: ath10k_dbg(ar, ATH10K_DBG_WMI, "Unknown eventid: %d\n", id); break; @@ -599,6 +602,31 @@ static int ath10k_wmi_tlv_op_pull_scan_ev(struct ath10k *ar, return 0; } +static int ath10k_wmi_tlv_op_pull_mgmt_tx_compl_ev( + struct ath10k *ar, struct sk_buff *skb, + struct wmi_tlv_mgmt_tx_compl_ev_arg *arg) +{ + const void **tb; + const struct wmi_tlv_mgmt_tx_compl_ev *ev; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_MGMT_TX_COMPL]; + + arg->desc_id = ev->desc_id; + arg->status = ev->status; + arg->pdev_id = ev->pdev_id; + + kfree(tb); + return 0; +} + static int ath10k_wmi_tlv_op_pull_mgmt_rx_ev(struct ath10k *ar, struct sk_buff *skb, struct wmi_mgmt_rx_ev_arg *arg) @@ -1505,6 +1533,12 @@ static struct sk_buff *ath10k_wmi_tlv_op_gen_init(struct ath10k *ar) cfg->keep_alive_pattern_size = __cpu_to_le32(0); cfg->max_tdls_concurrent_sleep_sta = __cpu_to_le32(1); cfg->max_tdls_concurrent_buffer_sta = __cpu_to_le32(1); + cfg->wmi_send_separate = __cpu_to_le32(0); + cfg->num_ocb_vdevs = __cpu_to_le32(0); + cfg->num_ocb_channels = __cpu_to_le32(0); + cfg->num_ocb_schedules = __cpu_to_le32(0); + cfg->host_capab = + __cpu_to_le32(WMI_TLV_TX_MSDU_ID_NEW_PARTITION_SUPPORT); ath10k_wmi_put_host_mem_chunks(ar, chunks); @@ -2482,6 +2516,30 @@ ath10k_wmi_tlv_op_gen_pdev_set_wmm(struct ath10k *ar, return skb; } +static int +ath10k_wmi_mgmt_tx_alloc_msdu_id(struct ath10k *ar, struct sk_buff *skb, + dma_addr_t paddr) +{ + struct ath10k_wmi *wmi = &ar->wmi; + struct ath10k_mgmt_tx_pkt_addr *pkt_addr; + int ret; + + pkt_addr = kmalloc(sizeof(*pkt_addr), GFP_ATOMIC); + if (!pkt_addr) + return -ENOMEM; + + pkt_addr->vaddr = skb; + pkt_addr->paddr = paddr; + + spin_lock_bh(&wmi->mgmt_tx_lock); + ret = idr_alloc(&wmi->mgmt_pending_tx, pkt_addr, 0, + wmi->mgmt_max_num_pending_tx, GFP_ATOMIC); + spin_unlock_bh(&wmi->mgmt_tx_lock); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt tx alloc msdu_id %d\n", ret); + return ret; +} + static struct sk_buff * ath10k_wmi_tlv_op_gen_request_stats(struct ath10k *ar, u32 stats_mask) { @@ -2504,21 +2562,20 @@ ath10k_wmi_tlv_op_gen_request_stats(struct ath10k *ar, u32 stats_mask) } static struct sk_buff * -ath10k_wmi_tlv_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu) +ath10k_wmi_tlv_op_gen_mgmt_tx_send(struct ath10k *ar, struct sk_buff *msdu, + dma_addr_t paddr) { struct ath10k_skb_cb *cb = ATH10K_SKB_CB(msdu); struct wmi_tlv_mgmt_tx_cmd *cmd; - struct wmi_tlv *tlv; struct ieee80211_hdr *hdr; + struct ath10k_vif *arvif; + u32 buf_len = msdu->len; + struct wmi_tlv *tlv; struct sk_buff *skb; + int desc_id, len; + u32 vdev_id; void *ptr; - int len; - u32 buf_len = (msdu->len < WMI_TX_DL_FRM_LEN) ? msdu->len : - WMI_TX_DL_FRM_LEN; u16 fc; - struct ath10k_vif *arvif; - dma_addr_t mgmt_frame_dma; - u32 vdev_id; hdr = (struct ieee80211_hdr *)msdu->data; fc = le16_to_cpu(hdr->frame_control); @@ -2549,24 +2606,21 @@ ath10k_wmi_tlv_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu) if (!skb) return ERR_PTR(-ENOMEM); + desc_id = ath10k_wmi_mgmt_tx_alloc_msdu_id(ar, msdu, paddr); + if (desc_id < 0) + goto msdu_id_alloc_fail; + ptr = (void *)skb->data; tlv = ptr; tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_MGMT_TX_CMD); tlv->len = __cpu_to_le16(sizeof(cmd->hdr)); cmd = (void *)tlv->value; cmd->hdr.vdev_id = vdev_id; - cmd->hdr.desc_id = 0; + cmd->hdr.desc_id = desc_id; cmd->hdr.chanfreq = 0; cmd->hdr.buf_len = __cpu_to_le32(buf_len); cmd->hdr.frame_len = __cpu_to_le32(msdu->len); - mgmt_frame_dma = dma_map_single(arvif->ar->dev, msdu->data, - msdu->len, DMA_TO_DEVICE); - if (!mgmt_frame_dma) - return ERR_PTR(-ENOMEM); - - cmd->hdr.paddr_lo = (uint32_t)(mgmt_frame_dma & 0xffffffff); - cmd->hdr.paddr_hi = (uint32_t)(upper_32_bits(mgmt_frame_dma) & - HTT_WCN3990_PADDR_MASK); + cmd->hdr.paddr = __cpu_to_le64(paddr); cmd->data_len = buf_len; cmd->data_tag = 0x11; @@ -2579,6 +2633,10 @@ ath10k_wmi_tlv_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu) memcpy(cmd->buf, msdu->data, buf_len); return skb; + +msdu_id_alloc_fail: + dev_kfree_skb(skb); + return ERR_PTR(desc_id); } static struct sk_buff * @@ -3014,6 +3072,37 @@ ath10k_wmi_tlv_op_gen_tdls_peer_update(struct ath10k *ar, return skb; } +static struct sk_buff * +ath10k_wmi_tlv_op_gen_csa_offload(struct ath10k *ar, u32 vdev_id, bool enable) +{ + struct wmi_csa_offload_enable_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + int len; + + len = sizeof(*cmd) + sizeof(*tlv); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_CSA_OFFLOAD_ENABLE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + + cmd->vdev_id = __cpu_to_le32(vdev_id); + if (enable) + cmd->csa_offload_enable |= + __cpu_to_le32(WMI_CSA_OFFLOAD_ENABLE); + else + cmd->csa_offload_enable |= + __cpu_to_le32(WMI_CSA_OFFLOAD_DISABLE); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi CSA offload for vdev: %d\n", vdev_id); + return skb; +} + static struct sk_buff * ath10k_wmi_op_gen_gtk_offload(struct ath10k *ar, struct ath10k_vif *arvif) { @@ -3059,13 +3148,14 @@ ath10k_wmi_tlv_op_gen_set_arp_ns_offload(struct ath10k *ar, void *ptr; int i; struct wmi_ns_arp_offload_req *arp = &arvif->arp_offload; + struct wmi_ns_arp_offload_req *ns = &arvif->ns_offload; struct wmi_ns_offload *ns_tuple; struct wmi_arp_offload *arp_tuple; len = sizeof(*cmd) + sizeof(*tlv) + - sizeof(*tlv) + WMI_MAX_NS_OFFLOADS * + sizeof(*tlv) + WMI_NS_ARP_OFFLOAD * (sizeof(struct wmi_ns_offload) + sizeof(*tlv)) + - sizeof(*tlv) + WMI_MAX_ARP_OFFLOADS * + sizeof(*tlv) + WMI_NS_ARP_OFFLOAD * (sizeof(struct wmi_arp_offload) + sizeof(*tlv)); skb = ath10k_wmi_alloc_skb(ar, len); @@ -3083,33 +3173,49 @@ ath10k_wmi_tlv_op_gen_set_arp_ns_offload(struct ath10k *ar, ptr += (sizeof(*tlv) + sizeof(*cmd)); tlv = ptr; tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT); - tlv->len = __cpu_to_le16(WMI_MAX_NS_OFFLOADS * + tlv->len = __cpu_to_le16(WMI_NS_ARP_OFFLOAD * (sizeof(struct wmi_ns_offload) + sizeof(*tlv))); ptr += sizeof(*tlv); tlv = ptr; - for (i = 0; i < WMI_MAX_NS_OFFLOADS; i++) { + for (i = 0; i < WMI_NS_ARP_OFFLOAD; i++) { tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_NS_OFFLOAD_TUPLE); tlv->len = __cpu_to_le16(sizeof(struct wmi_ns_offload)); ns_tuple = (struct wmi_ns_offload *)tlv->value; - ns_tuple->flags |= __cpu_to_le32(WMI_ARP_NS_OFFLOAD_DISABLE); + if (ns->enable_offload) { + ns_tuple->flags |= + __cpu_to_le32(WMI_ARP_NS_OFF_FLAGS_VALID); + if (ns->info.target_addr_valid.s6_addr[i]) { + memcpy(&ns_tuple->target_ipaddr[0], + &ns->info.target_addr[i], + sizeof(struct in6_addr)); + } + memcpy(&ns_tuple->solicitation_ipaddr, + &ns->info.self_addr[i], sizeof(struct in6_addr)); + if (ns->info.target_ipv6_ac.s6_addr[i] == IPV6_ADDR_ANY) + ns_tuple->flags |= + __cpu_to_le32(WMI_NSOFF_IPV6_ANYCAST); + } else { + ns_tuple->flags |= + __cpu_to_le32(WMI_ARP_NS_OFFLOAD_DISABLE); + } ptr += (sizeof(*tlv) + sizeof(struct wmi_ns_offload)); tlv = ptr; } tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT); - tlv->len = __cpu_to_le16(WMI_MAX_ARP_OFFLOADS * + tlv->len = __cpu_to_le16(WMI_NS_ARP_OFFLOAD * (sizeof(struct wmi_arp_offload) + sizeof(*tlv))); ptr += sizeof(*tlv); tlv = ptr; - for (i = 0; i < WMI_MAX_ARP_OFFLOADS; i++) { + for (i = 0; i < WMI_NS_ARP_OFFLOAD; i++) { tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_ARP_OFFLOAD_TUPLE); tlv->len = __cpu_to_le16(sizeof(struct wmi_arp_offload)); arp_tuple = (struct wmi_arp_offload *)tlv->value; if (arp->enable_offload && (i == 0)) { arp_tuple->flags |= - __cpu_to_le32(WMI_ARPOFF_FLAGS_VALID); + __cpu_to_le32(WMI_ARP_NS_OFF_FLAGS_VALID); memcpy(&arp_tuple->target_ipaddr, &arp->params.ipv4_addr, sizeof(arp_tuple->target_ipaddr)); @@ -3752,6 +3858,7 @@ static const struct wmi_ops wmi_tlv_ops = { .pull_scan = ath10k_wmi_tlv_op_pull_scan_ev, .pull_mgmt_rx = ath10k_wmi_tlv_op_pull_mgmt_rx_ev, + .pull_mgmt_tx_compl = ath10k_wmi_tlv_op_pull_mgmt_tx_compl_ev, .pull_ch_info = ath10k_wmi_tlv_op_pull_ch_info_ev, .pull_peer_delete_resp = ath10k_wmi_tlv_op_pull_peer_delete_ev, .pull_vdev_start = ath10k_wmi_tlv_op_pull_vdev_start_ev, @@ -3796,7 +3903,7 @@ static const struct wmi_ops wmi_tlv_ops = { .gen_pdev_set_wmm = ath10k_wmi_tlv_op_gen_pdev_set_wmm, .gen_request_stats = ath10k_wmi_tlv_op_gen_request_stats, .gen_force_fw_hang = ath10k_wmi_tlv_op_gen_force_fw_hang, - .gen_mgmt_tx = ath10k_wmi_tlv_op_gen_mgmt_tx, + .gen_mgmt_tx_send = ath10k_wmi_tlv_op_gen_mgmt_tx_send, .gen_dbglog_cfg = ath10k_wmi_tlv_op_gen_dbglog_cfg, .gen_pktlog_enable = ath10k_wmi_tlv_op_gen_pktlog_enable, .gen_pktlog_disable = ath10k_wmi_tlv_op_gen_pktlog_disable, @@ -3813,6 +3920,7 @@ static const struct wmi_ops wmi_tlv_ops = { .gen_sta_keepalive = ath10k_wmi_tlv_op_gen_sta_keepalive, .gen_set_arp_ns_offload = ath10k_wmi_tlv_op_gen_set_arp_ns_offload, .gen_gtk_offload = ath10k_wmi_op_gen_gtk_offload, + .gen_csa_offload = ath10k_wmi_tlv_op_gen_csa_offload, .gen_wow_enable = ath10k_wmi_tlv_op_gen_wow_enable, .gen_wow_add_wakeup_event = ath10k_wmi_tlv_op_gen_wow_add_wakeup_event, .gen_wow_host_wakeup_ind = ath10k_wmi_tlv_gen_wow_host_wakeup_ind, diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h index 18327daade8d..0d8543d20968 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h @@ -322,6 +322,7 @@ enum wmi_tlv_event_id { WMI_TLV_TBTTOFFSET_UPDATE_EVENTID, WMI_TLV_OFFLOAD_BCN_TX_STATUS_EVENTID, WMI_TLV_OFFLOAD_PROB_RESP_TX_STATUS_EVENTID, + WMI_TLV_MGMT_TX_COMPLETION_EVENTID, WMI_TLV_TX_DELBA_COMPLETE_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_BA_NEG), WMI_TLV_TX_ADDBA_COMPLETE_EVENTID, WMI_TLV_BA_RSP_SSN_EVENTID, @@ -898,6 +899,7 @@ enum wmi_tlv_tag { WMI_TLV_TAG_STRUCT_HL_1_0_SVC_OFFSET = 176, WMI_TLV_TAG_STRUCT_MGMT_TX_CMD = 0x1A6, + WMI_TLV_TAG_STRUCT_MGMT_TX_COMPL, WMI_TLV_TAG_STRUCT_PEER_DELETE_RESP_EVENT = 0x1C3, WMI_TLV_TAG_MAX @@ -1186,6 +1188,17 @@ struct wmi_tlv { u8 value[0]; } __packed; +struct ath10k_mgmt_tx_pkt_addr { + void *vaddr; + dma_addr_t paddr; +}; + +struct wmi_tlv_mgmt_tx_compl_ev { + __le32 desc_id; + __le32 status; + __le32 pdev_id; +}; + #define WMI_TLV_MGMT_RX_NUM_RSSI 4 struct wmi_tlv_mgmt_rx_ev { @@ -1254,6 +1267,8 @@ struct wmi_tlv_rdy_ev { __le32 status; } __packed; +#define WMI_TLV_TX_MSDU_ID_NEW_PARTITION_SUPPORT BIT(10) + struct wmi_tlv_resource_config { __le32 num_vdevs; __le32 num_peers; @@ -1291,6 +1306,11 @@ struct wmi_tlv_resource_config { __le32 keep_alive_pattern_size; __le32 max_tdls_concurrent_sleep_sta; __le32 max_tdls_concurrent_buffer_sta; + __le32 wmi_send_separate; + __le32 num_ocb_vdevs; + __le32 num_ocb_channels; + __le32 num_ocb_schedules; + __le32 host_capab; } __packed; struct wmi_tlv_init_cmd { @@ -1729,8 +1749,7 @@ struct wmi_tlv_mgmt_tx_hdr { __le32 vdev_id; __le32 desc_id; __le32 chanfreq; - __le32 paddr_lo; - __le32 paddr_hi; + __le64 paddr; __le32 frame_len; __le32 buf_len; } __packed; diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 514c8f4f39a9..ebcb32e41106 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2287,6 +2287,59 @@ int ath10k_wmi_tlv_event_peer_delete_resp(struct ath10k *ar, return 0; } +static int wmi_tlv_process_mgmt_tx_comp(struct ath10k *ar, u32 desc_id, + u32 status) +{ + struct ath10k_mgmt_tx_pkt_addr *pkt_addr; + struct ath10k_wmi *wmi = &ar->wmi; + struct ieee80211_tx_info *info; + struct sk_buff *msdu; + int ret = 0; + + spin_lock_bh(&wmi->mgmt_tx_lock); + pkt_addr = idr_find(&wmi->mgmt_pending_tx, desc_id); + if (!pkt_addr) { + ath10k_warn(ar, "received mgmt tx completion for invalid msdu_id: %d\n", + desc_id); + ret = -ENOENT; + goto tx_comp_process_done; + } + + msdu = pkt_addr->vaddr; + dma_unmap_single(ar->dev, pkt_addr->paddr, + msdu->len, DMA_FROM_DEVICE); + info = IEEE80211_SKB_CB(msdu); + if (!status) + info->flags |= IEEE80211_TX_STAT_ACK; + else + info->flags |= status; + ieee80211_tx_status_irqsafe(ar->hw, msdu); + ret = 0; + +tx_comp_process_done: + idr_remove(&wmi->mgmt_pending_tx, desc_id); + spin_unlock_bh(&wmi->mgmt_tx_lock); + + return ret; +} + +int ath10k_wmi_tlv_event_mgmt_tx_compl(struct ath10k *ar, struct sk_buff *skb) +{ + int ret; + struct wmi_tlv_mgmt_tx_compl_ev_arg arg; + + ret = ath10k_wmi_pull_mgmt_tx_compl(ar, skb, &arg); + if (ret) { + ath10k_warn(ar, "failed to parse mgmt comp event: %d\n", ret); + return ret; + } + + wmi_tlv_process_mgmt_tx_comp(ar, arg.desc_id, arg.status); + ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TLV_MGMT_TX_COMPLETION_EVENTID\n"); + + return 0; +} + int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_mgmt_rx_ev_arg arg = {}; @@ -4215,6 +4268,7 @@ void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, struct sk_buff *skb) return; } + ar->wow.wakeup_reason = ev.wake_reason; ath10k_dbg(ar, ATH10K_DBG_WMI, "wow wakeup host reason %s\n", wow_reason(ev.wake_reason)); } @@ -8307,6 +8361,11 @@ int ath10k_wmi_attach(struct ath10k *ar) INIT_WORK(&ar->svc_rdy_work, ath10k_wmi_event_service_ready_work); + if (QCA_REV_WCN3990(ar)) { + spin_lock_init(&ar->wmi.mgmt_tx_lock); + idr_init(&ar->wmi.mgmt_pending_tx); + } + return 0; } @@ -8326,8 +8385,32 @@ void ath10k_wmi_free_host_mem(struct ath10k *ar) ar->wmi.num_mem_chunks = 0; } +static int ath10k_wmi_mgmt_tx_clean_up_pending(int msdu_id, void *ptr, + void *ctx) +{ + struct ath10k_mgmt_tx_pkt_addr *pkt_addr = ptr; + struct ath10k *ar = ctx; + struct sk_buff *msdu; + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "force cleanup mgmt msdu_id %hu\n", msdu_id); + + msdu = pkt_addr->vaddr; + dma_unmap_single(ar->dev, pkt_addr->paddr, + msdu->len, DMA_FROM_DEVICE); + ieee80211_free_txskb(ar->hw, msdu); + + return 0; +} + void ath10k_wmi_detach(struct ath10k *ar) { + if (QCA_REV_WCN3990(ar)) { + idr_for_each(&ar->wmi.mgmt_pending_tx, + ath10k_wmi_mgmt_tx_clean_up_pending, ar); + idr_destroy(&ar->wmi.mgmt_pending_tx); + } + cancel_work_sync(&ar->svc_rdy_work); if (ar->svc_rdy_skb) diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 2694b6aa8b77..291969db777d 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -21,6 +21,7 @@ #include #include #include +#include #include /* @@ -2887,13 +2888,12 @@ struct wmi_start_scan_common { } __packed; /* ARP-NS offload data structure */ -#define WMI_NSOFF_MAX_TARGET_IPS 2 -#define WMI_MAX_NS_OFFLOADS 2 -#define WMI_MAX_ARP_OFFLOADS 2 -#define WMI_ARPOFF_FLAGS_VALID BIT(0) +#define WMI_NS_ARP_OFFLOAD 2 +#define WMI_ARP_NS_OFF_FLAGS_VALID BIT(0) #define WMI_IPV4_ARP_REPLY_OFFLOAD 0 #define WMI_ARP_NS_OFFLOAD_DISABLE 0 #define WMI_ARP_NS_OFFLOAD_ENABLE 1 +#define WMI_NSOFF_IPV6_ANYCAST BIT(3) struct wmi_ns_offload_info { struct in6_addr src_addr; @@ -2902,7 +2902,7 @@ struct wmi_ns_offload_info { struct wmi_mac_addr self_macaddr; u8 src_ipv6_addr_valid; struct in6_addr target_addr_valid; - struct in6_addr target_addr_ac_type; + struct in6_addr target_ipv6_ac; u8 slot_idx; } __packed; @@ -2914,13 +2914,13 @@ struct wmi_ns_arp_offload_req { struct in_addr ipv4_addr; struct in6_addr ipv6_addr; } params; - struct wmi_ns_offload_info offload_info; + struct wmi_ns_offload_info info; struct wmi_mac_addr bssid; } __packed; struct wmi_ns_offload { __le32 flags; - struct in6_addr target_ipaddr[WMI_NSOFF_MAX_TARGET_IPS]; + struct in6_addr target_ipaddr[WMI_NS_ARP_OFFLOAD]; struct in6_addr solicitation_ipaddr; struct in6_addr remote_ipaddr; struct wmi_mac_addr target_mac; @@ -6308,6 +6308,12 @@ struct wmi_peer_delete_resp_ev_arg { struct wmi_mac_addr peer_addr; }; +struct wmi_tlv_mgmt_tx_compl_ev_arg { + __le32 desc_id; + __le32 status; + __le32 pdev_id; +}; + struct wmi_mgmt_rx_ev_arg { __le32 channel; __le32 snr; @@ -6682,6 +6688,7 @@ int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb); int ath10k_wmi_tlv_event_peer_delete_resp(struct ath10k *ar, struct sk_buff *skb); int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb); +int ath10k_wmi_tlv_event_mgmt_tx_compl(struct ath10k *ar, struct sk_buff *skb); void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb); void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb); int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb); diff --git a/drivers/net/wireless/ath/ath10k/wow.c b/drivers/net/wireless/ath/ath10k/wow.c index d9fbabef52df..2280f47dc227 100644 --- a/drivers/net/wireless/ath/ath10k/wow.c +++ b/drivers/net/wireless/ath/ath10k/wow.c @@ -17,6 +17,7 @@ #include "mac.h" #include +#include #include "hif.h" #include "core.h" #include "debug.h" @@ -84,6 +85,7 @@ static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif, int ret, i; unsigned long wow_mask = 0; struct ath10k *ar = arvif->ar; + struct ieee80211_bss_conf *bss = &arvif->vif->bss_conf; const struct cfg80211_pkt_pattern *patterns = wowlan->patterns; int pattern_id = 0; @@ -102,18 +104,19 @@ static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif, __set_bit(WOW_RA_MATCH_EVENT, &wow_mask); break; case WMI_VDEV_TYPE_STA: - if (wowlan->disconnect) { - __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); - __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); - __set_bit(WOW_BMISS_EVENT, &wow_mask); - __set_bit(WOW_CSA_IE_EVENT, &wow_mask); + if (arvif->is_up && bss->assoc) { + if (wowlan->disconnect) { + __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); + __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); + __set_bit(WOW_BMISS_EVENT, &wow_mask); + __set_bit(WOW_CSA_IE_EVENT, &wow_mask); + } + + if (wowlan->magic_pkt) + __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask); + if (wowlan->gtk_rekey_failure) + __set_bit(WOW_GTK_ERR_EVENT, &wow_mask); } - - if (wowlan->magic_pkt) - __set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask); - - if (wowlan->gtk_rekey_failure) - __set_bit(WOW_GTK_ERR_EVENT, &wow_mask); break; default: break; @@ -229,6 +232,116 @@ static int ath10k_wow_wakeup(struct ath10k *ar) return 0; } +static int +ath10k_wow_fill_vdev_ns_offload_struct(struct ath10k_vif *arvif, + bool enable_offload) +{ + struct in6_addr addr[TARGET_NUM_STATIONS]; + struct wmi_ns_arp_offload_req *ns; + struct wireless_dev *wdev; + struct inet6_dev *in6_dev; + struct in6_addr addr_type; + struct inet6_ifaddr *ifa; + struct ifacaddr6 *ifaca; + struct list_head *addr_list; + u32 scope, count = 0; + int i; + + ns = &arvif->ns_offload; + if (!enable_offload) { + ns->offload_type = __cpu_to_le16(WMI_NS_ARP_OFFLOAD); + ns->enable_offload = __cpu_to_le16(WMI_ARP_NS_OFFLOAD_DISABLE); + return 0; + } + + wdev = ieee80211_vif_to_wdev(arvif->vif); + if (!wdev) + return -ENODEV; + + in6_dev = __in6_dev_get(wdev->netdev); + if (!in6_dev) + return -ENODEV; + + memset(&addr, 0, TARGET_NUM_STATIONS * sizeof(struct in6_addr)); + memset(&addr_type, 0, sizeof(struct in6_addr)); + + /* Unicast Addresses */ + read_lock_bh(&in6_dev->lock); + list_for_each(addr_list, &in6_dev->addr_list) { + if (count >= TARGET_NUM_STATIONS) { + read_unlock_bh(&in6_dev->lock); + return -EINVAL; + } + + ifa = list_entry(addr_list, struct inet6_ifaddr, if_list); + if (ifa->flags & IFA_F_DADFAILED) + continue; + scope = ipv6_addr_src_scope(&ifa->addr); + switch (scope) { + case IPV6_ADDR_SCOPE_GLOBAL: + case IPV6_ADDR_SCOPE_LINKLOCAL: + memcpy(&addr[count], &ifa->addr.s6_addr, + sizeof(ifa->addr.s6_addr)); + addr_type.s6_addr[count] = IPV6_ADDR_UNICAST; + count += 1; + break; + } + } + + /* Anycast Addresses */ + for (ifaca = in6_dev->ac_list; ifaca; ifaca = ifaca->aca_next) { + if (count >= TARGET_NUM_STATIONS) { + read_unlock_bh(&in6_dev->lock); + return -EINVAL; + } + + scope = ipv6_addr_src_scope(&ifaca->aca_addr); + switch (scope) { + case IPV6_ADDR_SCOPE_GLOBAL: + case IPV6_ADDR_SCOPE_LINKLOCAL: + memcpy(&addr[count], &ifaca->aca_addr, + sizeof(ifaca->aca_addr)); + addr_type.s6_addr[count] = IPV6_ADDR_ANY; + count += 1; + break; + } + } + read_unlock_bh(&in6_dev->lock); + + /* Filling up the request structure + * Filling the self_addr with solicited address + * A Solicited-Node multicast address is created by + * taking the last 24 bits of a unicast or anycast + * address and appending them to the prefix + * + * FF02:0000:0000:0000:0000:0001:FFXX:XXXX + * + * here XX is the unicast/anycast bits + */ + for (i = 0; i < count; i++) { + ns->info.self_addr[i].s6_addr[0] = 0xFF; + ns->info.self_addr[i].s6_addr[1] = 0x02; + ns->info.self_addr[i].s6_addr[11] = 0x01; + ns->info.self_addr[i].s6_addr[12] = 0xFF; + ns->info.self_addr[i].s6_addr[13] = addr[i].s6_addr[13]; + ns->info.self_addr[i].s6_addr[14] = addr[i].s6_addr[14]; + ns->info.self_addr[i].s6_addr[15] = addr[i].s6_addr[15]; + ns->info.slot_idx = i; + memcpy(&ns->info.target_addr[i], &addr[i], + sizeof(struct in6_addr)); + ns->info.target_addr_valid.s6_addr[i] = 1; + ns->info.target_ipv6_ac.s6_addr[i] = addr_type.s6_addr[i]; + memcpy(&ns->params.ipv6_addr, &ns->info.target_addr[i], + sizeof(struct in6_addr)); + } + + ns->offload_type = __cpu_to_le16(WMI_NS_ARP_OFFLOAD); + ns->enable_offload = __cpu_to_le16(WMI_ARP_NS_OFFLOAD_ENABLE); + ns->num_ns_offload_count = __cpu_to_le16(count); + + return 0; +} + static int ath10k_wow_fill_vdev_arp_offload_struct(struct ath10k_vif *arvif, bool enable_offload) @@ -289,6 +402,13 @@ static int ath10k_wow_enable_ns_arp_offload(struct ath10k *ar, bool offload) return ret; } + ret = ath10k_wow_fill_vdev_ns_offload_struct(arvif, offload); + if (ret) { + ath10k_err(ar, "NS-offload config failed, vdev: %d\n", + arvif->vdev_id); + return ret; + } + ret = ath10k_wmi_set_arp_ns_offload(ar, arvif); if (ret) { ath10k_err(ar, "failed to send offload cmd, vdev: %d\n", @@ -325,22 +445,6 @@ static int ath10k_config_wow_listen_interval(struct ath10k *ar) return 0; } -void ath10k_wow_op_set_rekey_data(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_gtk_rekey_data *data) -{ - struct ath10k *ar = hw->priv; - struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); - - mutex_lock(&ar->conf_mutex); - memcpy(&arvif->gtk_rekey_data.kek, data->kek, NL80211_KEK_LEN); - memcpy(&arvif->gtk_rekey_data.kck, data->kck, NL80211_KCK_LEN); - arvif->gtk_rekey_data.replay_ctr = - cpu_to_le64(be64_to_cpup((__be64 *)data->replay_ctr)); - arvif->gtk_rekey_data.valid = true; - mutex_unlock(&ar->conf_mutex); -} - static int ath10k_wow_config_gtk_offload(struct ath10k *ar, bool gtk_offload) { struct ath10k_vif *arvif; @@ -389,6 +493,13 @@ int ath10k_wow_op_suspend(struct ieee80211_hw *hw, goto exit; } + ret = ath10k_wow_cleanup(ar); + if (ret) { + ath10k_warn(ar, "failed to clear wow wakeup events: %d\n", + ret); + goto exit; + } + ret = ath10k_wow_config_gtk_offload(ar, true); if (ret) { ath10k_warn(ar, "failed to enable GTK offload: %d\n", ret); @@ -401,18 +512,11 @@ int ath10k_wow_op_suspend(struct ieee80211_hw *hw, goto disable_gtk_offload; } - ret = ath10k_wow_cleanup(ar); - if (ret) { - ath10k_warn(ar, "failed to clear wow wakeup events: %d\n", - ret); - goto disable_ns_arp_offload; - } - ret = ath10k_wow_set_wakeups(ar, wowlan); if (ret) { ath10k_warn(ar, "failed to set wow wakeup events: %d\n", ret); - goto cleanup; + goto disable_ns_arp_offload; } ret = ath10k_config_wow_listen_interval(ar); @@ -464,6 +568,46 @@ void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled) mutex_unlock(&ar->conf_mutex); } +static void ath10k_wow_op_report_wakeup_reason(struct ath10k *ar) +{ + struct cfg80211_wowlan_wakeup *wakeup = &ar->wow.wakeup; + struct ath10k_vif *arvif; + + memset(wakeup, 0, sizeof(struct cfg80211_wowlan_wakeup)); + switch (ar->wow.wakeup_reason) { + case WOW_REASON_UNSPECIFIED: + wakeup = NULL; + break; + case WOW_REASON_RECV_MAGIC_PATTERN: + wakeup->magic_pkt = true; + break; + case WOW_REASON_DEAUTH_RECVD: + case WOW_REASON_DISASSOC_RECVD: + case WOW_REASON_AP_ASSOC_LOST: + case WOW_REASON_CSA_EVENT: + wakeup->disconnect = true; + break; + case WOW_REASON_GTK_HS_ERR: + wakeup->gtk_rekey_failure = true; + break; + } + ar->wow.wakeup_reason = WOW_REASON_UNSPECIFIED; + + if (wakeup) { + wakeup->pattern_idx = -1; + list_for_each_entry(arvif, &ar->arvifs, list) { + ieee80211_report_wowlan_wakeup(arvif->vif, + wakeup, GFP_KERNEL); + if (wakeup->disconnect) + ieee80211_resume_disconnect(arvif->vif); + } + } else { + list_for_each_entry(arvif, &ar->arvifs, list) + ieee80211_report_wowlan_wakeup(arvif->vif, + NULL, GFP_KERNEL); + } +} + int ath10k_wow_op_resume(struct ieee80211_hw *hw) { struct ath10k *ar = hw->priv; @@ -518,6 +662,7 @@ int ath10k_wow_op_resume(struct ieee80211_hw *hw) } } + ath10k_wow_op_report_wakeup_reason(ar); mutex_unlock(&ar->conf_mutex); return ret; } @@ -534,8 +679,15 @@ int ath10k_wow_init(struct ath10k *ar) ar->wow.wowlan_support = ath10k_wowlan_support; ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns; ar->hw->wiphy->wowlan = &ar->wow.wowlan_support; - - device_set_wakeup_capable(ar->dev, true); + device_init_wakeup(ar->dev, true); return 0; } + +void ath10k_wow_deinit(struct ath10k *ar) +{ + if (test_bit(ATH10K_FW_FEATURE_WOWLAN_SUPPORT, + ar->running_fw->fw_file.fw_features) && + test_bit(WMI_SERVICE_WOW, ar->wmi.svc_map)) + device_init_wakeup(ar->dev, false); +} diff --git a/drivers/net/wireless/ath/ath10k/wow.h b/drivers/net/wireless/ath/ath10k/wow.h index ce79908cce19..2ca4ba4848c9 100644 --- a/drivers/net/wireless/ath/ath10k/wow.h +++ b/drivers/net/wireless/ath/ath10k/wow.h @@ -17,21 +17,21 @@ #define _WOW_H_ struct ath10k_wow { + u32 wakeup_reason; u32 max_num_patterns; struct completion wakeup_completed; + struct cfg80211_wowlan_wakeup wakeup; struct wiphy_wowlan_support wowlan_support; }; #ifdef CONFIG_PM int ath10k_wow_init(struct ath10k *ar); +void ath10k_wow_deinit(struct ath10k *ar); int ath10k_wow_op_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); int ath10k_wow_op_resume(struct ieee80211_hw *hw); void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled); -void ath10k_wow_op_set_rekey_data(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_gtk_rekey_data *data); #else static inline int ath10k_wow_init(struct ath10k *ar) @@ -39,5 +39,8 @@ static inline int ath10k_wow_init(struct ath10k *ar) return 0; } +void ath10k_wow_deinit(struct ath10k *ar) +{ +} #endif /* CONFIG_PM */ #endif /* _WOW_H_ */ diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c index a741c9c7d115..b29ae2be901f 100644 --- a/drivers/pci/host/pci-msm.c +++ b/drivers/pci/host/pci-msm.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 @@ -2421,6 +2421,13 @@ static void msm_pcie_sel_debug_testcase(struct msm_pcie_dev_t *dev, break; } + if (((base_sel - 1) >= MSM_PCIE_MAX_RES) || + (!dev->res[base_sel - 1].resource)) { + PCIE_DBG_FS(dev, "PCIe: RC%d Resource does not exist\n", + dev->rc_idx); + break; + } + PCIE_DBG_FS(dev, "base: %s: 0x%p\nwr_offset: 0x%x\nwr_mask: 0x%x\nwr_value: 0x%x\n", dev->res[base_sel - 1].name, @@ -2440,6 +2447,13 @@ static void msm_pcie_sel_debug_testcase(struct msm_pcie_dev_t *dev, break; case 13: /* dump all registers of base_sel */ + if (((base_sel - 1) >= MSM_PCIE_MAX_RES) || + (!dev->res[base_sel - 1].resource)) { + PCIE_DBG_FS(dev, "PCIe: RC%d Resource does not exist\n", + dev->rc_idx); + break; + } + if (!base_sel) { PCIE_DBG_FS(dev, "Invalid base_sel: 0x%x\n", base_sel); break; diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index f8b2b5987ea9..ec91cd17bf34 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -522,7 +522,7 @@ static ssize_t driver_override_store(struct device *dev, const char *buf, size_t count) { struct pci_dev *pdev = to_pci_dev(dev); - char *driver_override, *old = pdev->driver_override, *cp; + char *driver_override, *old, *cp; /* We need to keep extra room for a newline */ if (count >= (PAGE_SIZE - 1)) @@ -536,12 +536,15 @@ static ssize_t driver_override_store(struct device *dev, if (cp) *cp = '\0'; + device_lock(dev); + old = pdev->driver_override; if (strlen(driver_override)) { pdev->driver_override = driver_override; } else { kfree(driver_override); pdev->driver_override = NULL; } + device_unlock(dev); kfree(old); @@ -552,8 +555,12 @@ static ssize_t driver_override_show(struct device *dev, struct device_attribute *attr, char *buf) { struct pci_dev *pdev = to_pci_dev(dev); + ssize_t len; - return snprintf(buf, PAGE_SIZE, "%s\n", pdev->driver_override); + device_lock(dev); + len = snprintf(buf, PAGE_SIZE, "%s\n", pdev->driver_override); + device_unlock(dev); + return len; } static DEVICE_ATTR_RW(driver_override); diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig index 333d28e64087..92773ff33188 100644 --- a/drivers/platform/msm/Kconfig +++ b/drivers/platform/msm/Kconfig @@ -133,16 +133,6 @@ config IPA_UT The user interface to run and control the tests is debugfs file system. -config SSM - tristate "QTI Secure Service Module" - depends on QSEECOM - depends on MSM_SMD - help - Provides an interface for OEM driver to communicate with Trustzone - and modem for key exchange and mode change. - This driver uses Secure Channel Manager interface for trustzone - communication and communicates with modem over SMD channel. - config GPIO_USB_DETECT tristate "GPIO-based USB VBUS Detection" depends on POWER_SUPPLY diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile index 36800d9e6164..d1732ee04489 100644 --- a/drivers/platform/msm/Makefile +++ b/drivers/platform/msm/Makefile @@ -13,7 +13,6 @@ obj-$(CONFIG_EP_PCIE) += ep_pcie/ obj-$(CONFIG_GPIO_USB_DETECT) += gpio-usbdetect.o obj-$(CONFIG_MSM_11AD) += msm_11ad/ obj-$(CONFIG_SEEMP_CORE) += seemp_core/ -obj-$(CONFIG_SSM) += ssm.o obj-$(CONFIG_USB_BAM) += usb_bam.o obj-$(CONFIG_MSM_MHI_DEV) += mhi_dev/ obj-$(CONFIG_MSM_EXT_DISPLAY) += msm_ext_display.o diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c index d1028a162845..809f371c4c7b 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c @@ -83,6 +83,7 @@ const char *ipa_event_name[] = { __stringify(IPA_QUOTA_REACH), __stringify(IPA_SSR_BEFORE_SHUTDOWN), __stringify(IPA_SSR_AFTER_POWERUP), + __stringify(WLAN_FWR_SSR_BEFORE_SHUTDOWN), }; const char *ipa_hdr_l2_type_name[] = { @@ -426,6 +427,8 @@ static ssize_t ipa_read_hdr(struct file *file, char __user *ubuf, size_t count, list_for_each_entry(entry, &ipa_ctx->hdr_tbl.head_hdr_entry_list, link) { + if (entry->cookie != IPA_HDR_COOKIE) + continue; nbytes = scnprintf( dbg_buff, IPA_MAX_MSG_LEN, @@ -591,6 +594,14 @@ static int ipa_attrib_dump_eq(struct ipa_ipfltri_rule_eq *attrib) if (attrib->protocol_eq_present) pr_err("protocol:%d ", attrib->protocol_eq); + if (attrib->num_ihl_offset_range_16 > + IPA_IPFLTR_NUM_IHL_RANGE_16_EQNS) { + IPAERR_RL("num_ihl_offset_range_16 Max %d passed value %d\n", + IPA_IPFLTR_NUM_IHL_RANGE_16_EQNS, + attrib->num_ihl_offset_range_16); + return -EPERM; + } + for (i = 0; i < attrib->num_ihl_offset_range_16; i++) { pr_err( "(ihl_ofst_range16: ofst:%u lo:%u hi:%u) ", @@ -599,6 +610,12 @@ static int ipa_attrib_dump_eq(struct ipa_ipfltri_rule_eq *attrib) attrib->ihl_offset_range_16[i].range_high); } + if (attrib->num_offset_meq_32 > IPA_IPFLTR_NUM_MEQ_32_EQNS) { + IPAERR_RL("num_offset_meq_32 Max %d passed value %d\n", + IPA_IPFLTR_NUM_MEQ_32_EQNS, attrib->num_offset_meq_32); + return -EPERM; + } + for (i = 0; i < attrib->num_offset_meq_32; i++) { pr_err( "(ofst_meq32: ofst:%u mask:0x%x val:0x%x) ", @@ -620,6 +637,12 @@ static int ipa_attrib_dump_eq(struct ipa_ipfltri_rule_eq *attrib) attrib->ihl_offset_eq_16.value); } + if (attrib->num_ihl_offset_meq_32 > IPA_IPFLTR_NUM_IHL_MEQ_32_EQNS) { + IPAERR_RL("num_ihl_offset_meq_32 Max %d passed value %d\n", + IPA_IPFLTR_NUM_IHL_MEQ_32_EQNS, attrib->num_ihl_offset_meq_32); + return -EPERM; + } + for (i = 0; i < attrib->num_ihl_offset_meq_32; i++) { pr_err( "(ihl_ofst_meq32: ofts:%d mask:0x%x val:0x%x) ", @@ -628,6 +651,12 @@ static int ipa_attrib_dump_eq(struct ipa_ipfltri_rule_eq *attrib) attrib->ihl_offset_meq_32[i].value); } + if (attrib->num_offset_meq_128 > IPA_IPFLTR_NUM_MEQ_128_EQNS) { + IPAERR_RL("num_offset_meq_128 Max %d passed value %d\n", + IPA_IPFLTR_NUM_MEQ_128_EQNS, attrib->num_offset_meq_128); + return -EPERM; + } + for (i = 0; i < attrib->num_offset_meq_128; i++) { for (j = 0; j < 16; j++) { addr[j] = attrib->offset_meq_128[i].value[j]; @@ -797,11 +826,14 @@ static ssize_t ipa_read_flt(struct file *file, char __user *ubuf, size_t count, u32 rt_tbl_idx; u32 bitmap; bool eq; + int res = 0; tbl = &ipa_ctx->glob_flt_tbl[ip]; mutex_lock(&ipa_ctx->lock); i = 0; list_for_each_entry(entry, &tbl->head_flt_rule_list, link) { + if (entry->cookie != IPA_FLT_COOKIE) + continue; if (entry->rule.eq_attrib_type) { rt_tbl_idx = entry->rule.rt_tbl_idx; bitmap = entry->rule.eq_attrib.rule_eq_bitmap; @@ -820,10 +852,14 @@ static ssize_t ipa_read_flt(struct file *file, char __user *ubuf, size_t count, i, entry->rule.action, rt_tbl_idx); pr_err("attrib_mask:%08x retain_hdr:%d eq:%d ", bitmap, entry->rule.retain_hdr, eq); - if (eq) - ipa_attrib_dump_eq( + if (eq) { + res = ipa_attrib_dump_eq( &entry->rule.eq_attrib); - else + if (res) { + IPAERR_RL("failed read attrib eq\n"); + goto bail; + } + } else ipa_attrib_dump( &entry->rule.attrib, ip); i++; @@ -833,6 +869,8 @@ static ssize_t ipa_read_flt(struct file *file, char __user *ubuf, size_t count, tbl = &ipa_ctx->flt_tbl[j][ip]; i = 0; list_for_each_entry(entry, &tbl->head_flt_rule_list, link) { + if (entry->cookie != IPA_FLT_COOKIE) + continue; if (entry->rule.eq_attrib_type) { rt_tbl_idx = entry->rule.rt_tbl_idx; bitmap = entry->rule.eq_attrib.rule_eq_bitmap; @@ -852,18 +890,23 @@ static ssize_t ipa_read_flt(struct file *file, char __user *ubuf, size_t count, pr_err("attrib_mask:%08x retain_hdr:%d ", bitmap, entry->rule.retain_hdr); pr_err("eq:%d ", eq); - if (eq) - ipa_attrib_dump_eq( - &entry->rule.eq_attrib); - else + if (eq) { + res = ipa_attrib_dump_eq( + &entry->rule.eq_attrib); + if (res) { + IPAERR_RL("failed read attrib eq\n"); + goto bail; + } + } else ipa_attrib_dump( &entry->rule.attrib, ip); i++; } } +bail: mutex_unlock(&ipa_ctx->lock); - return 0; + return res; } static ssize_t ipa_read_stats(struct file *file, char __user *ubuf, diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c b/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c index 629924875d78..3d57c15943af 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_nat.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_nat.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 @@ -327,6 +327,11 @@ int ipa2_nat_init_cmd(struct ipa_ioc_v4_nat_init *init) size_t tmp; gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0); + if (!ipa_ctx->nat_mem.is_dev_init) { + IPAERR_RL("Nat table not initialized\n"); + return -EPERM; + } + IPADBG("\n"); if (init->table_entries == 0) { IPADBG("Table entries is zero\n"); @@ -572,6 +577,11 @@ int ipa2_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma) int ret = 0; gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0); + if (!ipa_ctx->nat_mem.is_dev_init) { + IPAERR_RL("Nat table not initialized\n"); + return -EPERM; + } + IPADBG("\n"); if (dma->entries <= 0) { IPAERR_RL("Invalid number of commands %d\n", @@ -760,6 +770,16 @@ int ipa2_nat_del_cmd(struct ipa_ioc_v4_nat_del *del) int result; gfp_t flag = GFP_KERNEL | (ipa_ctx->use_dma_zone ? GFP_DMA : 0); + if (!ipa_ctx->nat_mem.is_dev_init) { + IPAERR_RL("Nat table not initialized\n"); + return -EPERM; + } + + if (ipa_ctx->nat_mem.public_ip_addr) { + IPAERR_RL("Public IP addr not assigned and trying to delete\n"); + return -EPERM; + } + IPADBG("\n"); if (ipa_ctx->nat_mem.is_tmp_mem) { IPAERR("using temp memory during nat del\n"); diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c index 293a60a60881..c8ebba609ec9 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.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 @@ -51,6 +51,7 @@ int __ipa_generate_rt_hw_rule_v2(enum ipa_ip_type ip, u32 tmp[IPA_RT_FLT_HW_RULE_BUF_SIZE/4]; u8 *start; int pipe_idx; + struct ipa_hdr_entry *hdr_entry; if (buf == NULL) { memset(tmp, 0, (IPA_RT_FLT_HW_RULE_BUF_SIZE/4)); @@ -74,6 +75,18 @@ int __ipa_generate_rt_hw_rule_v2(enum ipa_ip_type ip, } rule_hdr->u.hdr.pipe_dest_idx = pipe_idx; rule_hdr->u.hdr.system = !ipa_ctx->hdr_tbl_lcl; + + /* Adding check to confirm still + * header entry present in header table or not + */ + + if (entry->hdr) { + hdr_entry = ipa_id_find(entry->rule.hdr_hdl); + if (!hdr_entry || hdr_entry->cookie != IPA_HDR_COOKIE) { + IPAERR_RL("Header entry already deleted\n"); + return -EPERM; + } + } if (entry->hdr) { if (entry->hdr->cookie == IPA_HDR_COOKIE) { rule_hdr->u.hdr.hdr_offset = @@ -140,6 +153,8 @@ int __ipa_generate_rt_hw_rule_v2_5(enum ipa_ip_type ip, u32 tmp[IPA_RT_FLT_HW_RULE_BUF_SIZE/4]; u8 *start; int pipe_idx; + struct ipa_hdr_entry *hdr_entry; + struct ipa_hdr_proc_ctx_entry *hdr_proc_entry; if (buf == NULL) { memset(tmp, 0, IPA_RT_FLT_HW_RULE_BUF_SIZE); @@ -162,6 +177,24 @@ int __ipa_generate_rt_hw_rule_v2_5(enum ipa_ip_type ip, return -EPERM; } rule_hdr->u.hdr_v2_5.pipe_dest_idx = pipe_idx; + /* Adding check to confirm still + * header entry present in header table or not + */ + + if (entry->hdr) { + hdr_entry = ipa_id_find(entry->rule.hdr_hdl); + if (!hdr_entry || hdr_entry->cookie != IPA_HDR_COOKIE) { + IPAERR_RL("Header entry already deleted\n"); + return -EPERM; + } + } else if (entry->proc_ctx) { + hdr_proc_entry = ipa_id_find(entry->rule.hdr_proc_ctx_hdl); + if (!hdr_proc_entry || + hdr_proc_entry->cookie != IPA_PROC_HDR_COOKIE) { + IPAERR_RL("Proc header entry already deleted\n"); + return -EPERM; + } + } if (entry->proc_ctx || (entry->hdr && entry->hdr->is_hdr_proc_ctx)) { struct ipa_hdr_proc_ctx_entry *proc_ctx; @@ -1130,6 +1163,8 @@ int __ipa_del_rt_rule(u32 rule_hdl) { struct ipa_rt_entry *entry; int id; + struct ipa_hdr_entry *hdr_entry; + struct ipa_hdr_proc_ctx_entry *hdr_proc_entry; entry = ipa_id_find(rule_hdl); @@ -1143,6 +1178,34 @@ int __ipa_del_rt_rule(u32 rule_hdl) return -EINVAL; } + /* Adding check to confirm still + * header entry present in header table or not + */ + + if (entry->hdr) { + hdr_entry = ipa_id_find(entry->rule.hdr_hdl); + if (!hdr_entry || hdr_entry->cookie != IPA_HDR_COOKIE) { + IPAERR_RL("Header entry already deleted\n"); + return -EINVAL; + } + } else if (entry->proc_ctx) { + hdr_proc_entry = ipa_id_find(entry->rule.hdr_proc_ctx_hdl); + if (!hdr_proc_entry || + hdr_proc_entry->cookie != IPA_PROC_HDR_COOKIE) { + IPAERR_RL("Proc header entry already deleted\n"); + return -EINVAL; + } + } + + if (!strcmp(entry->tbl->name, IPA_DFLT_RT_TBL_NAME)) { + IPADBG("Deleting rule from default rt table idx=%u\n", + entry->tbl->idx); + if (entry->tbl->rule_cnt == 1) { + IPAERR_RL("Default tbl last rule cannot be deleted\n"); + return -EINVAL; + } + } + if (entry->hdr) __ipa_release_hdr(entry->hdr->id); else if (entry->proc_ctx) @@ -1454,6 +1517,7 @@ static int __ipa_mdfy_rt_rule(struct ipa_rt_rule_mdfy *rtrule) { struct ipa_rt_entry *entry; struct ipa_hdr_entry *hdr = NULL; + struct ipa_hdr_entry *hdr_entry; if (rtrule->rule.hdr_hdl) { hdr = ipa_id_find(rtrule->rule.hdr_hdl); @@ -1474,6 +1538,17 @@ static int __ipa_mdfy_rt_rule(struct ipa_rt_rule_mdfy *rtrule) goto error; } + /* Adding check to confirm still + * header entry present in header table or not + */ + + if (entry->hdr) { + hdr_entry = ipa_id_find(entry->rule.hdr_hdl); + if (!hdr_entry || hdr_entry->cookie != IPA_HDR_COOKIE) { + IPAERR_RL("Header entry already deleted\n"); + return -EPERM; + } + } if (entry->hdr) entry->hdr->ref_cnt--; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c index 23f802425cf0..9eee3a90d1f0 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_wdi.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 @@ -582,7 +582,8 @@ static void ipa_save_uc_smmu_mapping_pa(int res_idx, phys_addr_t pa, { IPADBG("--res_idx=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", res_idx, &pa, iova, len); - wdi_res[res_idx].res = kzalloc(sizeof(struct ipa_wdi_res), GFP_KERNEL); + wdi_res[res_idx].res = kzalloc(sizeof(*wdi_res[res_idx].res), + GFP_KERNEL); if (!wdi_res[res_idx].res) BUG(); wdi_res[res_idx].nents = 1; @@ -608,8 +609,8 @@ static void ipa_save_uc_smmu_mapping_sgt(int res_idx, struct sg_table *sgt, return; } - wdi_res[res_idx].res = kcalloc(sgt->nents, sizeof(struct ipa_wdi_res), - GFP_KERNEL); + wdi_res[res_idx].res = kcalloc(sgt->nents, + sizeof(*wdi_res[res_idx].res), GFP_KERNEL); if (!wdi_res[res_idx].res) BUG(); wdi_res[res_idx].nents = sgt->nents; diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index a4963f918ae0..937f10e3c9ad 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -773,7 +773,7 @@ static int find_vchannel_name_index(const char *vchannel_name) { int i; - for (i = 0; i < MAX_NUM_OF_MUX_CHANNEL; i++) { + for (i = 0; i < rmnet_index; i++) { if (0 == strcmp(mux_channel[i].vchannel_name, vchannel_name)) return i; } @@ -1432,6 +1432,8 @@ static int ipa_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /* Extended IOCTLs */ case RMNET_IOCTL_EXTENDED: + if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN)) + return -EPERM; IPAWANDBG("get ioctl: RMNET_IOCTL_EXTENDED\n"); if (copy_from_user(&extend_ioctl_data, (u8 *)ifr->ifr_ifru.ifru_data, @@ -1660,6 +1662,7 @@ static int ipa_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 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 8c6bd48cfb2c..e2552d39c6ef 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.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 @@ -62,8 +62,16 @@ int ipa3_enable_data_path(u32 clnt_hdl) IPADBG("Enabling data path\n"); if (IPA_CLIENT_IS_CONS(ep->client)) { - memset(&holb_cfg, 0 , sizeof(holb_cfg)); - holb_cfg.en = IPA_HOLB_TMR_DIS; + memset(&holb_cfg, 0, sizeof(holb_cfg)); + /* + * Set HOLB on USB DPL CONS to avoid IPA stall + * if DPL client is not pulling the data + * on other end from IPA hw. + */ + if (ep->client == IPA_CLIENT_USB_DPL_CONS) + holb_cfg.en = IPA_HOLB_TMR_EN; + else + holb_cfg.en = IPA_HOLB_TMR_DIS; holb_cfg.tmr_val = 0; res = ipa3_cfg_ep_holb(clnt_hdl, &holb_cfg); } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c index 95b9ead06582..dea031cdaf82 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c @@ -64,6 +64,7 @@ const char *ipa3_event_name[] = { __stringify(IPA_QUOTA_REACH), __stringify(IPA_SSR_BEFORE_SHUTDOWN), __stringify(IPA_SSR_AFTER_POWERUP), + __stringify(WLAN_FWR_SSR_BEFORE_SHUTDOWN), }; const char *ipa3_hdr_l2_type_name[] = { @@ -351,6 +352,8 @@ static ssize_t ipa3_read_hdr(struct file *file, char __user *ubuf, size_t count, list_for_each_entry(entry, &ipa3_ctx->hdr_tbl.head_hdr_entry_list, link) { + if (entry->cookie != IPA_HDR_COOKIE) + continue; nbytes = scnprintf( dbg_buff, IPA_MAX_MSG_LEN, @@ -519,6 +522,12 @@ static int ipa3_attrib_dump_eq(struct ipa_ipfltri_rule_eq *attrib) if (attrib->tc_eq_present) pr_err("tc:%d ", attrib->tc_eq); + if (attrib->num_offset_meq_128 > IPA_IPFLTR_NUM_MEQ_128_EQNS) { + IPAERR_RL("num_offset_meq_128 Max %d passed value %d\n", + IPA_IPFLTR_NUM_MEQ_128_EQNS, attrib->num_offset_meq_128); + return -EPERM; + } + for (i = 0; i < attrib->num_offset_meq_128; i++) { for (j = 0; j < 16; j++) { addr[j] = attrib->offset_meq_128[i].value[j]; @@ -530,6 +539,12 @@ static int ipa3_attrib_dump_eq(struct ipa_ipfltri_rule_eq *attrib) mask, addr); } + if (attrib->num_offset_meq_32 > IPA_IPFLTR_NUM_MEQ_32_EQNS) { + IPAERR_RL("num_offset_meq_32 Max %d passed value %d\n", + IPA_IPFLTR_NUM_MEQ_32_EQNS, attrib->num_offset_meq_32); + return -EPERM; + } + for (i = 0; i < attrib->num_offset_meq_32; i++) pr_err( "(ofst_meq32: ofst:%u mask:0x%x val:0x%x) ", @@ -537,6 +552,12 @@ static int ipa3_attrib_dump_eq(struct ipa_ipfltri_rule_eq *attrib) attrib->offset_meq_32[i].mask, attrib->offset_meq_32[i].value); + if (attrib->num_ihl_offset_meq_32 > IPA_IPFLTR_NUM_IHL_MEQ_32_EQNS) { + IPAERR_RL("num_ihl_offset_meq_32 Max %d passed value %d\n", + IPA_IPFLTR_NUM_IHL_MEQ_32_EQNS, attrib->num_ihl_offset_meq_32); + return -EPERM; + } + for (i = 0; i < attrib->num_ihl_offset_meq_32; i++) pr_err( "(ihl_ofst_meq32: ofts:%d mask:0x%x val:0x%x) ", @@ -551,6 +572,14 @@ static int ipa3_attrib_dump_eq(struct ipa_ipfltri_rule_eq *attrib) attrib->metadata_meq32.mask, attrib->metadata_meq32.value); + if (attrib->num_ihl_offset_range_16 > + IPA_IPFLTR_NUM_IHL_RANGE_16_EQNS) { + IPAERR_RL("num_ihl_offset_range_16 Max %d passed value %d\n", + IPA_IPFLTR_NUM_IHL_RANGE_16_EQNS, + attrib->num_ihl_offset_range_16); + return -EPERM; + } + for (i = 0; i < attrib->num_ihl_offset_range_16; i++) pr_err( "(ihl_ofst_range16: ofst:%u lo:%u hi:%u) ", @@ -743,7 +772,11 @@ static ssize_t ipa3_read_rt_hw(struct file *file, char __user *ubuf, pr_err("rule_id:%u prio:%u retain_hdr:%u ", rules[rl].id, rules[rl].priority, rules[rl].retain_hdr); - ipa3_attrib_dump_eq(&rules[rl].eq_attrib); + res = ipa3_attrib_dump_eq(&rules[rl].eq_attrib); + if (res) { + IPAERR_RL("failed read attrib eq\n"); + goto bail; + } } pr_err("=== Routing Table %d = Non-Hashable Rules ===\n", tbl); @@ -774,7 +807,11 @@ static ssize_t ipa3_read_rt_hw(struct file *file, char __user *ubuf, pr_err("rule_id:%u prio:%u retain_hdr:%u\n", rules[rl].id, rules[rl].priority, rules[rl].retain_hdr); - ipa3_attrib_dump_eq(&rules[rl].eq_attrib); + res = ipa3_attrib_dump_eq(&rules[rl].eq_attrib); + if (res) { + IPAERR_RL("failed read attrib eq\n"); + goto bail; + } } pr_err("\n"); } @@ -848,6 +885,7 @@ static ssize_t ipa3_read_flt(struct file *file, char __user *ubuf, size_t count, u32 rt_tbl_idx; u32 bitmap; bool eq; + int res = 0; mutex_lock(&ipa3_ctx->lock); @@ -857,6 +895,8 @@ static ssize_t ipa3_read_flt(struct file *file, char __user *ubuf, size_t count, tbl = &ipa3_ctx->flt_tbl[j][ip]; i = 0; list_for_each_entry(entry, &tbl->head_flt_rule_list, link) { + if (entry->cookie != IPA_FLT_COOKIE) + continue; if (entry->rule.eq_attrib_type) { rt_tbl_idx = entry->rule.rt_tbl_idx; bitmap = entry->rule.eq_attrib.rule_eq_bitmap; @@ -878,18 +918,23 @@ static ssize_t ipa3_read_flt(struct file *file, char __user *ubuf, size_t count, pr_err("hashable:%u rule_id:%u max_prio:%u prio:%u ", entry->rule.hashable, entry->rule_id, entry->rule.max_prio, entry->prio); - if (eq) - ipa3_attrib_dump_eq( + if (eq) { + res = ipa3_attrib_dump_eq( &entry->rule.eq_attrib); - else + if (res) { + IPAERR_RL("failed read attrib eq\n"); + goto bail; + } + } else ipa3_attrib_dump( &entry->rule.attrib, ip); i++; } } +bail: mutex_unlock(&ipa3_ctx->lock); - return 0; + return res; } static ssize_t ipa3_read_flt_hw(struct file *file, char __user *ubuf, @@ -940,7 +985,11 @@ static ssize_t ipa3_read_flt_hw(struct file *file, char __user *ubuf, bitmap, rules[rl].rule.retain_hdr); pr_err("rule_id:%u prio:%u ", rules[rl].id, rules[rl].priority); - ipa3_attrib_dump_eq(&rules[rl].rule.eq_attrib); + res = ipa3_attrib_dump_eq(&rules[rl].rule.eq_attrib); + if (res) { + IPAERR_RL("failed read attrib eq\n"); + goto bail; + } } pr_err("=== Filtering Table ep:%d = Non-Hashable Rules ===\n", @@ -964,7 +1013,11 @@ static ssize_t ipa3_read_flt_hw(struct file *file, char __user *ubuf, bitmap, rules[rl].rule.retain_hdr); pr_err("rule_id:%u prio:%u ", rules[rl].id, rules[rl].priority); - ipa3_attrib_dump_eq(&rules[rl].rule.eq_attrib); + res = ipa3_attrib_dump_eq(&rules[rl].rule.eq_attrib); + if (res) { + IPAERR_RL("failed read attrib eq\n"); + goto bail; + } } pr_err("\n"); } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c index 136d58b4dd9d..e53f014a870f 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.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 @@ -61,8 +61,10 @@ static int ipa3_generate_flt_hw_rule(enum ipa_ip_type ip, gen_params.rule = (const struct ipa_flt_rule *)&entry->rule; res = ipahal_flt_generate_hw_rule(&gen_params, &entry->hw_len, buf); - if (res) + if (res) { IPAERR_RL("failed to generate flt h/w rule\n"); + return res; + } return 0; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c index 28628b1e4e29..b399a3f9ead5 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.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 @@ -795,6 +795,12 @@ int ipa3_nat_del_cmd(struct ipa_ioc_v4_nat_del *del) } memset(&desc, 0, sizeof(desc)); + + if (!ipa3_ctx->nat_mem.is_dev_init) { + IPAERR_RL("NAT hasn't been initialized\n"); + return -EPERM; + } + /* NO-OP IC for ensuring that IPA pipeline is empty */ nop_cmd_pyld = ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c index 8c33633ae164..db11fc696567 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.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 @@ -708,7 +708,7 @@ int ipa3_qmi_filter_request_ex_send( return -EINVAL; } - for (i = 0; i < req->filter_spec_ex_list_len-1; i++) { + for (i = 0; i < req->filter_spec_ex_list_len; i++) { if ((req->filter_spec_ex_list[i].ip_type != QMI_IPA_IP_TYPE_V4_V01) && (req->filter_spec_ex_list[i].ip_type != diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c index 7c81622450bc..6b23754260ce 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.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 @@ -50,6 +50,8 @@ static int ipa_generate_rt_hw_rule(enum ipa_ip_type ip, struct ipa3_rt_entry *entry, u8 *buf) { struct ipahal_rt_rule_gen_params gen_params; + struct ipa3_hdr_entry *hdr_entry; + struct ipa3_hdr_proc_ctx_entry *hdr_proc_entry; int res = 0; memset(&gen_params, 0, sizeof(gen_params)); @@ -69,6 +71,26 @@ static int ipa_generate_rt_hw_rule(enum ipa_ip_type ip, return -EPERM; } + + /* Adding check to confirm still + * header entry present in header table or not + */ + + if (entry->hdr) { + hdr_entry = ipa3_id_find(entry->rule.hdr_hdl); + if (!hdr_entry || hdr_entry->cookie != IPA_HDR_COOKIE) { + IPAERR_RL("Header entry already deleted\n"); + return -EPERM; + } + } else if (entry->proc_ctx) { + hdr_proc_entry = ipa3_id_find(entry->rule.hdr_proc_ctx_hdl); + if (!hdr_proc_entry || + hdr_proc_entry->cookie != IPA_PROC_HDR_COOKIE) { + IPAERR_RL("Proc header entry already deleted\n"); + return -EPERM; + } + } + if (entry->proc_ctx || (entry->hdr && entry->hdr->is_hdr_proc_ctx)) { struct ipa3_hdr_proc_ctx_entry *proc_ctx; proc_ctx = (entry->proc_ctx) ? : entry->hdr->proc_ctx; @@ -1214,6 +1236,8 @@ int __ipa3_del_rt_rule(u32 rule_hdl) { struct ipa3_rt_entry *entry; int id; + struct ipa3_hdr_entry *hdr_entry; + struct ipa3_hdr_proc_ctx_entry *hdr_proc_entry; entry = ipa3_id_find(rule_hdl); @@ -1227,6 +1251,34 @@ int __ipa3_del_rt_rule(u32 rule_hdl) return -EINVAL; } + /* Adding check to confirm still + * header entry present in header table or not + */ + + if (entry->hdr) { + hdr_entry = ipa3_id_find(entry->rule.hdr_hdl); + if (!hdr_entry || hdr_entry->cookie != IPA_HDR_COOKIE) { + IPAERR_RL("Header entry already deleted\n"); + return -EINVAL; + } + } else if (entry->proc_ctx) { + hdr_proc_entry = ipa3_id_find(entry->rule.hdr_proc_ctx_hdl); + if (!hdr_proc_entry || + hdr_proc_entry->cookie != IPA_PROC_HDR_COOKIE) { + IPAERR_RL("Proc header entry already deleted\n"); + return -EINVAL; + } + } + + if (!strcmp(entry->tbl->name, IPA_DFLT_RT_TBL_NAME)) { + IPADBG("Deleting rule from default rt table idx=%u\n", + entry->tbl->idx); + if (entry->tbl->rule_cnt == 1) { + IPAERR_RL("Default tbl last rule cannot be deleted\n"); + return -EINVAL; + } + } + if (entry->hdr) __ipa3_release_hdr(entry->hdr->id); else if (entry->proc_ctx) @@ -1544,7 +1596,8 @@ static int __ipa_mdfy_rt_rule(struct ipa_rt_rule_mdfy *rtrule) struct ipa3_rt_entry *entry; struct ipa3_hdr_entry *hdr = NULL; struct ipa3_hdr_proc_ctx_entry *proc_ctx = NULL; - + struct ipa3_hdr_entry *hdr_entry; + struct ipa3_hdr_proc_ctx_entry *hdr_proc_entry; if (rtrule->rule.hdr_hdl) { hdr = ipa3_id_find(rtrule->rule.hdr_hdl); if ((hdr == NULL) || (hdr->cookie != IPA_HDR_COOKIE)) { @@ -1571,6 +1624,25 @@ static int __ipa_mdfy_rt_rule(struct ipa_rt_rule_mdfy *rtrule) goto error; } + /* Adding check to confirm still + * header entry present in header table or not + */ + + if (entry->hdr) { + hdr_entry = ipa3_id_find(entry->rule.hdr_hdl); + if (!hdr_entry || hdr_entry->cookie != IPA_HDR_COOKIE) { + IPAERR_RL("Header entry already deleted\n"); + return -EPERM; + } + } else if (entry->proc_ctx) { + hdr_proc_entry = ipa3_id_find(entry->rule.hdr_proc_ctx_hdl); + if (!hdr_proc_entry || + hdr_proc_entry->cookie != IPA_PROC_HDR_COOKIE) { + IPAERR_RL("Proc header entry already deleted\n"); + return -EPERM; + } + } + if (entry->hdr) entry->hdr->ref_cnt--; if (entry->proc_ctx) diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c index b92c08d3c133..341bb0cd7c77 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.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 @@ -1207,8 +1207,6 @@ int ipa3_connect_wdi_pipe(struct ipa_wdi_in_params *in, IPADBG("Skipping endpoint configuration.\n"); } - ipa3_enable_data_path(ipa_ep_idx); - out->clnt_hdl = ipa_ep_idx; if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(in->sys.client)) @@ -1314,6 +1312,7 @@ int ipa3_enable_wdi_pipe(u32 clnt_hdl) struct ipa3_ep_context *ep; union IpaHwWdiCommonChCmdData_t enable; struct ipa_ep_cfg_holb holb_cfg; + struct ipahal_reg_endp_init_rsrc_grp rsrc_grp; if (clnt_hdl >= ipa3_ctx->ipa_num_pipes || ipa3_ctx->ep[clnt_hdl].valid == 0) { @@ -1346,6 +1345,20 @@ int ipa3_enable_wdi_pipe(u32 clnt_hdl) goto uc_timeout; } + /* Assign the resource group for pipe */ + memset(&rsrc_grp, 0, sizeof(rsrc_grp)); + rsrc_grp.rsrc_grp = ipa_get_ep_group(ep->client); + if (rsrc_grp.rsrc_grp == -1) { + IPAERR("invalid group for client %d\n", ep->client); + WARN_ON(1); + return -EFAULT; + } + + IPADBG("Setting group %d for pipe %d\n", + rsrc_grp.rsrc_grp, clnt_hdl); + ipahal_write_reg_n_fields(IPA_ENDP_INIT_RSRC_GRP_n, clnt_hdl, + &rsrc_grp); + if (IPA_CLIENT_IS_CONS(ep->client)) { memset(&holb_cfg, 0 , sizeof(holb_cfg)); holb_cfg.en = IPA_HOLB_TMR_DIS; diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c index d86848d00ea2..21705a8e7e5f 100644 --- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c @@ -792,7 +792,7 @@ static int find_vchannel_name_index(const char *vchannel_name) { int i; - for (i = 0; i < MAX_NUM_OF_MUX_CHANNEL; i++) { + for (i = 0; i < rmnet_ipa3_ctx->rmnet_index; i++) { if (strcmp(rmnet_ipa3_ctx->mux_channel[i].vchannel_name, vchannel_name) == 0) return i; @@ -1562,6 +1562,8 @@ static int ipa3_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /* Extended IOCTLs */ case RMNET_IOCTL_EXTENDED: + if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN)) + return -EPERM; IPAWANDBG("get ioctl: RMNET_IOCTL_EXTENDED\n"); if (copy_from_user(&extend_ioctl_data, (u8 *)ifr->ifr_ifru.ifru_data, @@ -1737,6 +1739,7 @@ static int ipa3_wwan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 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/sps/sps.c b/drivers/platform/msm/sps/sps.c index 59a9b911553c..0f6b66719ef2 100644 --- a/drivers/platform/msm/sps/sps.c +++ b/drivers/platform/msm/sps/sps.c @@ -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 @@ -1006,8 +1006,6 @@ static void sps_device_de_init(void) "sps:%s:BAMs are still registered", __func__); sps_map_de_init(); - - kfree(sps); } sps_mem_de_init(); @@ -2993,6 +2991,7 @@ static struct platform_driver msm_sps_driver = { .name = SPS_DRV_NAME, .owner = THIS_MODULE, .of_match_table = msm_sps_match, + .suppress_bind_attrs = true, }, .remove = msm_sps_remove, }; diff --git a/drivers/platform/msm/ssm.c b/drivers/platform/msm/ssm.c deleted file mode 100644 index 6a2909b8c5f4..000000000000 --- a/drivers/platform/msm/ssm.c +++ /dev/null @@ -1,516 +0,0 @@ -/* Copyright (c) 2013-2014, 2016 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. - */ -/* - * QTI Secure Service Module(SSM) driver - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../misc/qseecom_kernel.h" -#include "ssm.h" - -/* Macros */ -#define SSM_DEV_NAME "ssm" -#define MPSS_SUBSYS 0 -#define SSM_INFO_CMD_ID 1 -#define MAX_APP_NAME_SIZE 32 -#define SSM_MSG_LEN 200 -#define SSM_MSG_FIELD_LEN 11 -#define ATOM_MSG_LEN (SSM_MSG_FIELD_LEN + SSM_MSG_LEN + 40) - -#define TZAPP_NAME "SsmApp" -#define CHANNEL_NAME "SSM_RTR_MODEM_APPS" - -/* SSM driver structure.*/ -struct ssm_driver { - int32_t app_status; - int32_t update_status; - unsigned char *channel_name; - unsigned char *smd_buffer; - struct device *dev; - smd_channel_t *ch; - struct work_struct ipc_work; - struct mutex mutex; - struct qseecom_handle *qseecom_handle; - struct tzapp_get_mode_info_rsp *resp; - bool key_status; - bool ready; -}; - -static struct ssm_driver *ssm_drv; - -static unsigned int getint(char *buff, unsigned long *res) -{ - char value[SSM_MSG_FIELD_LEN]; - - memcpy(value, buff, SSM_MSG_FIELD_LEN); - value[SSM_MSG_FIELD_LEN - 1] = '\0'; - - return kstrtoul(skip_spaces(value), 10, res); -} - -/* - * Setup CMD/RSP pointers. - */ -static void setup_cmd_rsp_buffers(struct qseecom_handle *handle, void **cmd, - int *cmd_len, void **resp, int *resp_len) -{ - *cmd = handle->sbuf; - if (*cmd_len & QSEECOM_ALIGN_MASK) - *cmd_len = QSEECOM_ALIGN(*cmd_len); - - *resp = handle->sbuf + *cmd_len; - if (*resp_len & QSEECOM_ALIGN_MASK) - *resp_len = QSEECOM_ALIGN(*resp_len); -} - -/* - * Send packet to modem over SMD channel. - */ -static int update_modem(enum ssm_ipc_req ipc_req, struct ssm_driver *ssm, - int length, char *data) -{ - unsigned int packet_len = length + SSM_MSG_FIELD_LEN; - int rc = 0, count; - - snprintf(ssm->smd_buffer, SSM_MSG_FIELD_LEN + 1, "%10u|", ipc_req); - memcpy(ssm->smd_buffer + SSM_MSG_FIELD_LEN, data, length); - - if (smd_write_avail(ssm->ch) < packet_len) { - dev_err(ssm->dev, "Not enough space dropping request\n"); - rc = -ENOSPC; - goto out; - } - - count = smd_write(ssm->ch, ssm->smd_buffer, packet_len); - if (count < packet_len) { - dev_err(ssm->dev, "smd_write failed for %d\n", ipc_req); - rc = -EIO; - } - -out: - return rc; -} - -/* - * Header Format - * Each member of header is of 10 byte (ASCII). - * Each entry is separated by '|' delimiter. - * |<-10 bytes->|<-10 bytes->| - * |-------------------------| - * | IPC code | error code | - * |-------------------------| - * - */ -static int decode_packet(char *buffer, struct ssm_common_msg *pkt) -{ - int rc; - - rc = getint(buffer, (unsigned long *)&pkt->ipc_req); - if (rc < 0) - return -EINVAL; - - buffer += SSM_MSG_FIELD_LEN; - rc = getint(buffer, (unsigned long *)&pkt->err_code); - if (rc < 0) - return -EINVAL; - - dev_dbg(ssm_drv->dev, "req %d error code %d\n", - pkt->ipc_req, pkt->err_code); - return 0; -} - -static void process_message(struct ssm_common_msg pkt, struct ssm_driver *ssm) -{ - - switch (pkt.ipc_req) { - - case SSM_MTOA_MODE_UPDATE_STATUS: - if (pkt.err_code) { - dev_err(ssm->dev, "Modem mode update failed\n"); - ssm->update_status = FAILED; - } else - ssm->update_status = SUCCESS; - - dev_dbg(ssm->dev, "Modem mode update status %d\n", - pkt.err_code); - break; - - default: - dev_dbg(ssm->dev, "Invalid message\n"); - break; - }; -} - -/* - * Work function to handle and process packets coming from modem. - */ -static void ssm_app_modem_work_fn(struct work_struct *work) -{ - int sz, rc; - struct ssm_common_msg pkt; - struct ssm_driver *ssm; - - ssm = container_of(work, struct ssm_driver, ipc_work); - - mutex_lock(&ssm->mutex); - sz = smd_cur_packet_size(ssm->ch); - if ((sz < SSM_MSG_FIELD_LEN) || (sz > ATOM_MSG_LEN)) { - dev_dbg(ssm_drv->dev, "Garbled message size\n"); - goto unlock; - } - - if (smd_read_avail(ssm->ch) < sz) { - dev_err(ssm_drv->dev, "SMD error data in channel\n"); - goto unlock; - } - - if (smd_read(ssm->ch, ssm->smd_buffer, sz) != sz) { - dev_err(ssm_drv->dev, "Incomplete data\n"); - goto unlock; - } - - rc = decode_packet(ssm->smd_buffer, &pkt); - if (rc < 0) { - dev_err(ssm_drv->dev, "Corrupted header\n"); - goto unlock; - } - - process_message(pkt, ssm); - -unlock: - mutex_unlock(&ssm->mutex); -} - -/* - * MODEM-APPS smd channel callback function. - */ -static void modem_request(void *ctxt, unsigned event) -{ - struct ssm_driver *ssm; - - ssm = (struct ssm_driver *)ctxt; - - switch (event) { - case SMD_EVENT_OPEN: - case SMD_EVENT_CLOSE: - dev_dbg(ssm->dev, "SMD port status changed\n"); - break; - case SMD_EVENT_DATA: - if (smd_read_avail(ssm->ch) > 0) - schedule_work(&ssm->ipc_work); - break; - }; -} - -/* - * Load SSM application in TZ and start application: - */ -static int ssm_load_app(struct ssm_driver *ssm) -{ - int rc; - - /* Load the APP */ - rc = qseecom_start_app(&ssm->qseecom_handle, TZAPP_NAME, SZ_4K); - if (rc < 0) { - dev_err(ssm->dev, "Unable to load SSM app\n"); - ssm->app_status = FAILED; - return -EIO; - } - - ssm->app_status = SUCCESS; - return 0; -} - -static struct ssm_platform_data *populate_ssm_pdata(struct device *dev) -{ - struct ssm_platform_data *pdata; - - pdata = devm_kzalloc(dev, sizeof(struct ssm_platform_data), - GFP_KERNEL); - if (!pdata) - return NULL; - - pdata->need_key_exchg = - of_property_read_bool(dev->of_node, "qcom,need-keyexhg"); - - pdata->channel_name = CHANNEL_NAME; - - return pdata; -} - -static int ssm_probe(struct platform_device *pdev) -{ - int rc; - struct ssm_platform_data *pdata; - struct ssm_driver *drv; - - if (pdev->dev.of_node) - pdata = populate_ssm_pdata(&pdev->dev); - else - pdata = pdev->dev.platform_data; - - if (!pdata) { - dev_err(&pdev->dev, "Empty platform data\n"); - return -ENOMEM; - } - - drv = devm_kzalloc(&pdev->dev, sizeof(struct ssm_driver), - GFP_KERNEL); - if (!drv) - return -ENOMEM; - - /* Allocate response buffer */ - drv->resp = devm_kzalloc(&pdev->dev, - sizeof(struct tzapp_get_mode_info_rsp), - GFP_KERNEL); - if (!drv->resp) { - devm_kfree(&pdev->dev, drv); - rc = -ENOMEM; - goto exit; - } - - /* Initialize the driver structure */ - drv->app_status = RETRY; - drv->ready = false; - drv->update_status = FAILED; - mutex_init(&drv->mutex); - drv->key_status = !pdata->need_key_exchg; - drv->channel_name = (char *)pdata->channel_name; - INIT_WORK(&drv->ipc_work, ssm_app_modem_work_fn); - - /* Allocate memory for smd buffer */ - drv->smd_buffer = devm_kzalloc(&pdev->dev, - (sizeof(char) * ATOM_MSG_LEN), GFP_KERNEL); - if (!drv->smd_buffer) { - devm_kfree(&pdev->dev, drv->resp); - devm_kfree(&pdev->dev, drv); - rc = -ENOMEM; - goto exit; - } - - drv->dev = &pdev->dev; - ssm_drv = drv; - platform_set_drvdata(pdev, ssm_drv); - - dev_dbg(&pdev->dev, "probe success\n"); - return 0; - -exit: - mutex_destroy(&drv->mutex); - platform_set_drvdata(pdev, NULL); - return rc; - -} - -static int ssm_remove(struct platform_device *pdev) -{ - - if (!ssm_drv) - return 0; - /* - * Step to exit - * 1. set ready to 0 (oem access closed). - * 2. Close SMD modem connection closed. - * 3. cleanup ion. - */ - ssm_drv->ready = false; - smd_close(ssm_drv->ch); - flush_work(&ssm_drv->ipc_work); - - /* Shutdown tzapp */ - dev_dbg(&pdev->dev, "Shutting down TZapp\n"); - qseecom_shutdown_app(&ssm_drv->qseecom_handle); - - /* freeing the memory allocations - for the driver and the buffer */ - devm_kfree(&pdev->dev, ssm_drv->smd_buffer); - devm_kfree(&pdev->dev, ssm_drv->resp); - devm_kfree(&pdev->dev, ssm_drv); - - return 0; -} - -static struct of_device_id ssm_match_table[] = { - { - .compatible = "qcom,ssm", - }, - {} -}; - -static struct platform_driver ssm_pdriver = { - .probe = ssm_probe, - .remove = ssm_remove, - .driver = { - .name = SSM_DEV_NAME, - .owner = THIS_MODULE, - .of_match_table = ssm_match_table, - }, -}; -module_platform_driver(ssm_pdriver); - -/* - * Interface for external OEM driver. - * This interface supports following functionalities: - * 1. Set mode (encrypted mode and it's length is passed as parameter). - * 2. Set mode from TZ (read encrypted mode from TZ) - * 3. Get status of mode update. - * - */ -int ssm_oem_driver_intf(int cmd, char *mode, int len) -{ - int rc, req_len, resp_len; - struct tzapp_get_mode_info_req *get_mode_req; - struct tzapp_get_mode_info_rsp *get_mode_resp; - - /* If ssm_drv is NULL, probe failed */ - if (!ssm_drv) - return -ENODEV; - - mutex_lock(&ssm_drv->mutex); - - if (ssm_drv->app_status == RETRY) { - /* Load TZAPP */ - rc = ssm_load_app(ssm_drv); - if (rc) { - rc = -ENODEV; - goto unlock; - } - } else if (ssm_drv->app_status == FAILED) { - rc = -ENODEV; - goto unlock; - } - - /* Open modem SMD interface */ - if (!ssm_drv->ready) { - rc = smd_named_open_on_edge(ssm_drv->channel_name, - SMD_APPS_MODEM, - &ssm_drv->ch, - ssm_drv, - modem_request); - if (rc) { - rc = -EAGAIN; - goto unlock; - } else - ssm_drv->ready = true; - } - - /* Try again modem key-exchange not yet done.*/ - if (!ssm_drv->key_status) { - rc = -EAGAIN; - goto unlock; - } - - /* Set return status to success */ - rc = 0; - - switch (cmd) { - case SSM_READY: - break; - - case SSM_MODE_INFO_READY: - ssm_drv->update_status = RETRY; - /* Fill command structure */ - req_len = sizeof(struct tzapp_get_mode_info_req); - resp_len = sizeof(struct tzapp_get_mode_info_rsp); - setup_cmd_rsp_buffers(ssm_drv->qseecom_handle, - (void **)&get_mode_req, &req_len, - (void **)&get_mode_resp, &resp_len); - get_mode_req->tzapp_ssm_cmd = GET_ENC_MODE; - - rc = qseecom_set_bandwidth(ssm_drv->qseecom_handle, 1); - if (rc) { - ssm_drv->update_status = FAILED; - dev_err(ssm_drv->dev, "set bandwidth failed\n"); - rc = -EIO; - break; - } - rc = qseecom_send_command(ssm_drv->qseecom_handle, - (void *)get_mode_req, req_len, - (void *)get_mode_resp, resp_len); - if (rc || get_mode_resp->status) { - ssm_drv->update_status = FAILED; - break; - } - rc = qseecom_set_bandwidth(ssm_drv->qseecom_handle, 0); - if (rc) { - ssm_drv->update_status = FAILED; - dev_err(ssm_drv->dev, "clear bandwidth failed\n"); - rc = -EIO; - break; - } - - if (get_mode_resp->enc_mode_len > ENC_MODE_MAX_SIZE) { - ssm_drv->update_status = FAILED; - rc = -EINVAL; - break; - } - /* Send mode_info to modem */ - rc = update_modem(SSM_ATOM_MODE_UPDATE, ssm_drv, - get_mode_resp->enc_mode_len, - get_mode_resp->enc_mode_info); - if (rc) - ssm_drv->update_status = FAILED; - break; - - case SSM_SET_MODE: - ssm_drv->update_status = RETRY; - - if (len > ENC_MODE_MAX_SIZE) { - ssm_drv->update_status = FAILED; - rc = -EINVAL; - break; - } - memcpy(ssm_drv->resp->enc_mode_info, mode, len); - ssm_drv->resp->enc_mode_len = len; - - /* Send mode_info to modem */ - rc = update_modem(SSM_ATOM_MODE_UPDATE, ssm_drv, - ssm_drv->resp->enc_mode_len, - ssm_drv->resp->enc_mode_info); - if (rc) - ssm_drv->update_status = FAILED; - break; - - case SSM_GET_MODE_STATUS: - rc = ssm_drv->update_status; - break; - - default: - rc = -EINVAL; - dev_err(ssm_drv->dev, "Invalid command\n"); - break; - }; - -unlock: - mutex_unlock(&ssm_drv->mutex); - return rc; -} - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("QTI Secure Service Module"); - diff --git a/drivers/platform/msm/ssm.h b/drivers/platform/msm/ssm.h deleted file mode 100644 index ee4f1bc1d83f..000000000000 --- a/drivers/platform/msm/ssm.h +++ /dev/null @@ -1,90 +0,0 @@ -/* Copyright (c) 2013-2014, 2016 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 __SSM_H_ -#define __SSM_H_ - -#define MAX_APP_NAME_SIZE 32 -#define ENC_MODE_MAX_SIZE 200 - -/* tzapp response.*/ -enum tz_response { - RESULT_SUCCESS = 0, - RESULT_FAILURE = 0xFFFFFFFF, -}; - -/* tzapp command list.*/ -enum tz_commands { - ENC_MODE, - GET_ENC_MODE, - KEY_EXCHANGE = 11, -}; - -/* MODEM/SSM command list.*/ -enum ssm_ipc_req { - SSM_IPC_MIN = 0x0000AAAB, - SSM_ATOM_MODE_UPDATE, - SSM_MTOA_MODE_UPDATE_STATUS = SSM_IPC_MIN + 4, - SSM_INVALID_REQ, -}; - -/* OEM request commands list.*/ -enum oem_req { - SSM_READY, - SSM_MODE_INFO_READY, - SSM_SET_MODE, - SSM_GET_MODE_STATUS, - SSM_INVALID, -}; - -/* Modem mode update status.*/ -enum modem_mode_status { - SUCCESS, - RETRY, - FAILED = -1, -}; - -/* tzapp encode mode request.*/ -__packed struct tzapp_mode_enc_req { - uint32_t tzapp_ssm_cmd; - uint8_t mode_info[4]; -}; - -/* tzapp encode mode response.*/ -__packed struct tzapp_mode_enc_rsp { - uint32_t tzapp_ssm_cmd; - uint8_t enc_mode_info[ENC_MODE_MAX_SIZE]; - uint32_t enc_mode_len; - uint32_t status; -}; - -/* tzapp get mode request.*/ -__packed struct tzapp_get_mode_info_req { - uint32_t tzapp_ssm_cmd; -}; - -/* tzapp get mode response.*/ -__packed struct tzapp_get_mode_info_rsp { - uint32_t tzapp_ssm_cmd; - uint8_t enc_mode_info[ENC_MODE_MAX_SIZE]; - uint32_t enc_mode_len; - uint32_t status; -}; - -/* Modem/SSM packet format.*/ -struct ssm_common_msg { - enum ssm_ipc_req ipc_req; - int err_code; - -}; - -#endif diff --git a/drivers/power/qcom/lpm-stats.c b/drivers/power/qcom/lpm-stats.c index d3cafc411a77..3e08ac72073f 100644 --- a/drivers/power/qcom/lpm-stats.c +++ b/drivers/power/qcom/lpm-stats.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-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 @@ -682,11 +682,14 @@ static void cleanup_stats(struct lpm_stats *stats) { struct list_head *centry = NULL; struct lpm_stats *pos = NULL; + struct lpm_stats *n = NULL; centry = &stats->child; - list_for_each_entry_reverse(pos, centry, sibling) { - if (!list_empty(&pos->child)) + list_for_each_entry_safe_reverse(pos, n, centry, sibling) { + if (!list_empty(&pos->child)) { cleanup_stats(pos); + continue; + } list_del_init(&pos->child); diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index c34788e9d2fd..e151d5848df2 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -1883,7 +1883,7 @@ static int smb2_chg_config_init(struct smb2 *chip) break; case PM660_SUBTYPE: chip->chg.smb_version = PM660_SUBTYPE; - chip->chg.wa_flags |= BOOST_BACK_WA | OTG_WA; + chip->chg.wa_flags |= BOOST_BACK_WA | OTG_WA | OV_IRQ_WA_BIT; chg->param.freq_buck = pm660_params.freq_buck; chg->param.freq_boost = pm660_params.freq_boost; chg->chg_freq.freq_5V = 650; diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 5d92ed142d67..fe2a9ea29b5e 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -731,6 +731,7 @@ static void smblib_uusb_removal(struct smb_charger *chg) vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0); vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0); vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0); + vote(chg->hvdcp_hw_inov_dis_votable, OV_VOTER, false, 0); cancel_delayed_work_sync(&chg->hvdcp_detect_work); @@ -2139,6 +2140,18 @@ static int smblib_dm_pulse(struct smb_charger *chg) return rc; } +static int smblib_force_vbus_voltage(struct smb_charger *chg, u8 val) +{ + int rc; + + rc = smblib_masked_write(chg, CMD_HVDCP_2_REG, val, val); + if (rc < 0) + smblib_err(chg, "Couldn't write to CMD_HVDCP_2_REG rc=%d\n", + rc); + + return rc; +} + int smblib_dp_dm(struct smb_charger *chg, int val) { int target_icl_ua, rc = 0; @@ -2190,6 +2203,21 @@ int smblib_dp_dm(struct smb_charger *chg, int val) smblib_dbg(chg, PR_PARALLEL, "ICL DOWN ICL=%d reduction=%d\n", target_icl_ua, chg->usb_icl_delta_ua); break; + case POWER_SUPPLY_DP_DM_FORCE_5V: + rc = smblib_force_vbus_voltage(chg, FORCE_5V_BIT); + if (rc < 0) + pr_err("Failed to force 5V\n"); + break; + case POWER_SUPPLY_DP_DM_FORCE_9V: + rc = smblib_force_vbus_voltage(chg, FORCE_9V_BIT); + if (rc < 0) + pr_err("Failed to force 9V\n"); + break; + case POWER_SUPPLY_DP_DM_FORCE_12V: + rc = smblib_force_vbus_voltage(chg, FORCE_12V_BIT); + if (rc < 0) + pr_err("Failed to force 12V\n"); + break; case POWER_SUPPLY_DP_DM_ICL_UP: default: break; @@ -3564,6 +3592,33 @@ static void smblib_handle_sdp_enumeration_done(struct smb_charger *chg, rising ? "rising" : "falling"); } +#define MICRO_10P3V 10300000 +static void smblib_check_ov_condition(struct smb_charger *chg) +{ + union power_supply_propval pval = {0, }; + int rc; + + if (chg->wa_flags & OV_IRQ_WA_BIT) { + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get current voltage, rc=%d\n", + rc); + return; + } + + if (pval.intval > MICRO_10P3V) { + smblib_err(chg, "USBIN OV detected\n"); + vote(chg->hvdcp_hw_inov_dis_votable, OV_VOTER, true, + 0); + pval.intval = POWER_SUPPLY_DP_DM_FORCE_5V; + rc = power_supply_set_property(chg->batt_psy, + POWER_SUPPLY_PROP_DP_DM, &pval); + return; + } + } +} + #define QC3_PULSES_FOR_6V 5 #define QC3_PULSES_FOR_9V 20 #define QC3_PULSES_FOR_12V 35 @@ -3573,6 +3628,7 @@ static void smblib_hvdcp_adaptive_voltage_change(struct smb_charger *chg) u8 stat; int pulses; + smblib_check_ov_condition(chg); power_supply_changed(chg->usb_main_psy); if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP) { rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat); @@ -3970,6 +4026,7 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) /* reset hvdcp voters */ vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0); vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, true, 0); + vote(chg->hvdcp_hw_inov_dis_votable, OV_VOTER, false, 0); /* reset power delivery voters */ vote(chg->pd_allowed_votable, PD_VOTER, false, 0); diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index eb3ee03e8010..8d0fab3dae25 100755 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -66,6 +66,7 @@ enum print_reason { #define OTG_DELAY_VOTER "OTG_DELAY_VOTER" #define USBIN_I_VOTER "USBIN_I_VOTER" #define WEAK_CHARGER_VOTER "WEAK_CHARGER_VOTER" +#define OV_VOTER "OV_VOTER" #define CONFIG_CHARGER_RUNIN #define THERMAL_CONFIG_FB 1 @@ -87,6 +88,7 @@ enum { TYPEC_CC2_REMOVAL_WA_BIT = BIT(2), QC_AUTH_INTERRUPT_WA_BIT = BIT(3), OTG_WA = BIT(4), + OV_IRQ_WA_BIT = BIT(5), }; enum smb_irq_index { diff --git a/drivers/pwm/pwm-qpnp.c b/drivers/pwm/pwm-qpnp.c index 5e808150a3dd..d8112ba53b36 100644 --- a/drivers/pwm/pwm-qpnp.c +++ b/drivers/pwm/pwm-qpnp.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 diff --git a/drivers/regulator/cprh-kbss-regulator.c b/drivers/regulator/cprh-kbss-regulator.c index 4d6d63e6d887..f7a18611b5d2 100644 --- a/drivers/regulator/cprh-kbss-regulator.c +++ b/drivers/regulator/cprh-kbss-regulator.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 @@ -78,9 +78,10 @@ struct cprh_kbss_fuses { * Fuse combos 8 - 15 map to CPR fusing revision 0 - 7 with speed bin fuse = 1. * Fuse combos 16 - 23 map to CPR fusing revision 0 - 7 with speed bin fuse = 2. * Fuse combos 24 - 31 map to CPR fusing revision 0 - 7 with speed bin fuse = 3. + * Fuse combos 32 - 39 map to CPR fusing revision 0 - 7 with speed bin fuse = 4. */ #define CPRH_MSM8998_KBSS_FUSE_COMBO_COUNT 32 -#define CPRH_SDM660_KBSS_FUSE_COMBO_COUNT 32 +#define CPRH_SDM660_KBSS_FUSE_COMBO_COUNT 40 #define CPRH_SDM630_KBSS_FUSE_COMBO_COUNT 24 /* diff --git a/drivers/regulator/qpnp-lcdb-regulator.c b/drivers/regulator/qpnp-lcdb-regulator.c index 27f2d9851c2a..28c7da33afe0 100644 --- a/drivers/regulator/qpnp-lcdb-regulator.c +++ b/drivers/regulator/qpnp-lcdb-regulator.c @@ -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 @@ -198,6 +198,7 @@ struct qpnp_lcdb { struct regmap *regmap; struct pmic_revid_data *pmic_rev_id; u32 base; + u32 wa_flags; int sc_irq; /* TTW params */ @@ -265,6 +266,10 @@ enum lcdb_settings_index { LCDB_SETTING_MAX, }; +enum lcdb_wa_flags { + NCP_SCP_DISABLE_WA = BIT(0), +}; + static u32 soft_start_us[] = { 0, 500, @@ -606,9 +611,7 @@ static int qpnp_lcdb_enable_wa(struct qpnp_lcdb *lcdb) return rc; } - /* execute the below for rev1.1 */ - if (lcdb->pmic_rev_id->rev3 == PM660L_V1P1_REV3 && - lcdb->pmic_rev_id->rev4 == PM660L_V1P1_REV4) { + if (lcdb->wa_flags & NCP_SCP_DISABLE_WA) { /* * delay to make sure that the MID pin – ie the * output of the LCDB boost – returns to 0V @@ -1720,11 +1723,27 @@ static int qpnp_lcdb_init_bst(struct qpnp_lcdb *lcdb) return 0; } +static void qpnp_lcdb_pmic_config(struct qpnp_lcdb *lcdb) +{ + switch (lcdb->pmic_rev_id->pmic_subtype) { + case PM660L_SUBTYPE: + if (lcdb->pmic_rev_id->rev4 < PM660L_V2P0_REV4) + lcdb->wa_flags |= NCP_SCP_DISABLE_WA; + break; + default: + break; + } + + pr_debug("LCDB wa_flags = 0x%2x\n", lcdb->wa_flags); +} + static int qpnp_lcdb_hw_init(struct qpnp_lcdb *lcdb) { int rc = 0; u8 val = 0; + qpnp_lcdb_pmic_config(lcdb); + rc = qpnp_lcdb_init_bst(lcdb); if (rc < 0) { pr_err("Failed to initialize BOOST rc=%d\n", rc); @@ -1743,8 +1762,7 @@ static int qpnp_lcdb_hw_init(struct qpnp_lcdb *lcdb) return rc; } - if (lcdb->sc_irq >= 0 && - lcdb->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE) { + if (lcdb->sc_irq >= 0 && !(lcdb->wa_flags & NCP_SCP_DISABLE_WA)) { lcdb->sc_count = 0; rc = devm_request_threaded_irq(lcdb->dev, lcdb->sc_irq, NULL, qpnp_lcdb_sc_irq_handler, IRQF_ONESHOT, diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 9bb934ed2a7a..dbe70002b4fb 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -362,6 +362,14 @@ int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) } EXPORT_SYMBOL_GPL(rtc_set_alarm); +static void rtc_alarm_disable(struct rtc_device *rtc) +{ + if (!rtc->ops || !rtc->ops->alarm_irq_enable) + return; + + rtc->ops->alarm_irq_enable(rtc->dev.parent, false); +} + /* Called once per device from rtc_device_register */ int rtc_initialize_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) { @@ -389,7 +397,11 @@ int rtc_initialize_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) rtc->aie_timer.enabled = 1; timerqueue_add(&rtc->timerqueue, &rtc->aie_timer.node); + } else if (alarm->enabled && (rtc_tm_to_ktime(now).tv64 >= + rtc->aie_timer.node.expires.tv64)){ + rtc_alarm_disable(rtc); } + mutex_unlock(&rtc->ops_lock); return err; } @@ -782,14 +794,6 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer) return 0; } -static void rtc_alarm_disable(struct rtc_device *rtc) -{ - if (!rtc->ops || !rtc->ops->alarm_irq_enable) - return; - - rtc->ops->alarm_irq_enable(rtc->dev.parent, false); -} - /** * rtc_timer_remove - Removes a rtc_timer from the rtc_device timerqueue * @rtc rtc device diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 11d550cf414b..b821c23b42c4 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -2051,11 +2051,12 @@ sg_get_rq_mark(Sg_fd * sfp, int pack_id) if ((1 == resp->done) && (!resp->sg_io_owned) && ((-1 == pack_id) || (resp->header.pack_id == pack_id))) { resp->done = 2; /* guard against other readers */ - break; + write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + return resp; } } write_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return resp; + return NULL; } /* always adds to end of list */ diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 94d28c4ae210..22fba331d3d6 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.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 @@ -2215,8 +2215,6 @@ static int icnss_driver_event_fw_ready_ind(void *data) 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); @@ -2298,29 +2296,6 @@ static int icnss_driver_event_unregister_driver(void *data) return 0; } -static int icnss_call_driver_remove(struct icnss_priv *priv) -{ - icnss_pr_dbg("Calling driver remove state: 0x%lx\n", priv->state); - - clear_bit(ICNSS_FW_READY, &priv->state); - - if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state)) - return 0; - - if (!priv->ops || !priv->ops->remove) - return 0; - - set_bit(ICNSS_DRIVER_UNLOADING, &penv->state); - penv->ops->remove(&priv->pdev->dev); - - clear_bit(ICNSS_DRIVER_UNLOADING, &penv->state); - clear_bit(ICNSS_DRIVER_PROBED, &priv->state); - - icnss_hw_power_off(penv); - - return 0; -} - static int icnss_fw_crashed(struct icnss_priv *priv, struct icnss_event_pd_service_down_data *event_data) { @@ -2356,10 +2331,7 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, goto out; } - if (event_data->crashed) - icnss_fw_crashed(priv, event_data); - else - icnss_call_driver_remove(priv); + icnss_fw_crashed(priv, event_data); out: kfree(data); @@ -2808,7 +2780,8 @@ static int icnss_enable_recovery(struct icnss_priv *priv) return 0; } -int icnss_register_driver(struct icnss_driver_ops *ops) +int __icnss_register_driver(struct icnss_driver_ops *ops, + struct module *owner, const char *mod_name) { int ret = 0; @@ -2839,7 +2812,7 @@ int icnss_register_driver(struct icnss_driver_ops *ops) out: return ret; } -EXPORT_SYMBOL(icnss_register_driver); +EXPORT_SYMBOL(__icnss_register_driver); int icnss_unregister_driver(struct icnss_driver_ops *ops) { @@ -2865,7 +2838,7 @@ int icnss_unregister_driver(struct icnss_driver_ops *ops) } EXPORT_SYMBOL(icnss_unregister_driver); -int icnss_ce_request_irq(unsigned int ce_id, +int icnss_ce_request_irq(struct device *dev, unsigned int ce_id, irqreturn_t (*handler)(int, void *), unsigned long flags, const char *name, void *ctx) { @@ -2873,7 +2846,7 @@ int icnss_ce_request_irq(unsigned int ce_id, unsigned int irq; struct ce_irq_list *irq_entry; - if (!penv || !penv->pdev) { + if (!penv || !penv->pdev || !dev) { ret = -ENODEV; goto out; } @@ -2912,13 +2885,13 @@ int icnss_ce_request_irq(unsigned int ce_id, } EXPORT_SYMBOL(icnss_ce_request_irq); -int icnss_ce_free_irq(unsigned int ce_id, void *ctx) +int icnss_ce_free_irq(struct device *dev, unsigned int ce_id, void *ctx) { int ret = 0; unsigned int irq; struct ce_irq_list *irq_entry; - if (!penv || !penv->pdev) { + if (!penv || !penv->pdev || !dev) { ret = -ENODEV; goto out; } @@ -2948,11 +2921,11 @@ int icnss_ce_free_irq(unsigned int ce_id, void *ctx) } EXPORT_SYMBOL(icnss_ce_free_irq); -void icnss_enable_irq(unsigned int ce_id) +void icnss_enable_irq(struct device *dev, unsigned int ce_id) { unsigned int irq; - if (!penv || !penv->pdev) { + if (!penv || !penv->pdev || !dev) { icnss_pr_err("Platform driver not initialized\n"); return; } @@ -2972,11 +2945,11 @@ void icnss_enable_irq(unsigned int ce_id) } EXPORT_SYMBOL(icnss_enable_irq); -void icnss_disable_irq(unsigned int ce_id) +void icnss_disable_irq(struct device *dev, unsigned int ce_id) { unsigned int irq; - if (!penv || !penv->pdev) { + if (!penv || !penv->pdev || !dev) { icnss_pr_err("Platform driver not initialized\n"); return; } @@ -2997,9 +2970,9 @@ void icnss_disable_irq(unsigned int ce_id) } EXPORT_SYMBOL(icnss_disable_irq); -int icnss_get_soc_info(struct icnss_soc_info *info) +int icnss_get_soc_info(struct device *dev, struct icnss_soc_info *info) { - if (!penv) { + if (!penv || !dev) { icnss_pr_err("Platform driver not initialized\n"); return -EINVAL; } @@ -3019,10 +2992,19 @@ int icnss_get_soc_info(struct icnss_soc_info *info) } EXPORT_SYMBOL(icnss_get_soc_info); -int icnss_set_fw_log_mode(uint8_t fw_log_mode) +int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode) { int ret; + if (!dev) + return -ENODEV; + + if (test_bit(ICNSS_FW_DOWN, &penv->state)) { + icnss_pr_err("FW down, ignoring fw_log_mode state: 0x%lx\n", + penv->state); + return -EINVAL; + } + icnss_pr_dbg("FW log mode: %u\n", fw_log_mode); ret = wlfw_ini_send_sync_msg(fw_log_mode); @@ -3105,7 +3087,7 @@ int icnss_athdiag_write(struct device *dev, uint32_t offset, } EXPORT_SYMBOL(icnss_athdiag_write); -int icnss_wlan_enable(struct icnss_wlan_enable_cfg *config, +int icnss_wlan_enable(struct device *dev, struct icnss_wlan_enable_cfg *config, enum icnss_driver_mode mode, const char *host_version) { @@ -3113,6 +3095,15 @@ int icnss_wlan_enable(struct icnss_wlan_enable_cfg *config, u32 i; int ret; + if (!dev) + return -ENODEV; + + if (test_bit(ICNSS_FW_DOWN, &penv->state)) { + icnss_pr_err("FW down, ignoring wlan_enable state: 0x%lx\n", + penv->state); + return -EINVAL; + } + icnss_pr_dbg("Mode: %d, config: %p, host_version: %s\n", mode, config, host_version); @@ -3179,23 +3170,26 @@ int icnss_wlan_enable(struct icnss_wlan_enable_cfg *config, } EXPORT_SYMBOL(icnss_wlan_enable); -int icnss_wlan_disable(enum icnss_driver_mode mode) +int icnss_wlan_disable(struct device *dev, enum icnss_driver_mode mode) { + if (!dev) + return -ENODEV; + return wlfw_wlan_mode_send_sync_msg(QMI_WLFW_OFF_V01); } EXPORT_SYMBOL(icnss_wlan_disable); -bool icnss_is_qmi_disable(void) +bool icnss_is_qmi_disable(struct device *dev) { return test_bit(SKIP_QMI, &quirks) ? true : false; } EXPORT_SYMBOL(icnss_is_qmi_disable); -int icnss_get_ce_id(int irq) +int icnss_get_ce_id(struct device *dev, int irq) { int i; - if (!penv || !penv->pdev) + if (!penv || !penv->pdev || !dev) return -ENODEV; for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) { @@ -3209,11 +3203,11 @@ int icnss_get_ce_id(int irq) } EXPORT_SYMBOL(icnss_get_ce_id); -int icnss_get_irq(int ce_id) +int icnss_get_irq(struct device *dev, int ce_id) { int irq; - if (!penv || !penv->pdev) + if (!penv || !penv->pdev || !dev) return -ENODEV; if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) @@ -3647,7 +3641,7 @@ static int icnss_test_mode_fw_test_off(struct icnss_priv *priv) goto out; } - icnss_wlan_disable(ICNSS_OFF); + icnss_wlan_disable(&priv->pdev->dev, ICNSS_OFF); ret = icnss_hw_power_off(priv); @@ -3688,7 +3682,7 @@ static int icnss_test_mode_fw_test(struct icnss_priv *priv, set_bit(ICNSS_FW_TEST_MODE, &priv->state); - ret = icnss_wlan_enable(NULL, mode, NULL); + ret = icnss_wlan_enable(&priv->pdev->dev, NULL, mode, NULL); if (ret) goto power_off; diff --git a/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c b/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c index 305f31f31bf8..8cc3a4ecbaf8 100644 --- a/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c +++ b/drivers/soc/qcom/msm_bus/msm_bus_fabric_adhoc.c @@ -972,10 +972,8 @@ static struct device *msm_bus_device_init( bus_node = kzalloc(sizeof(struct msm_bus_node_device_type), GFP_KERNEL); if (!bus_node) { - MSM_BUS_ERR("%s:Bus node alloc failed\n", __func__); - kfree(bus_dev); - bus_dev = NULL; - goto exit_device_init; + ret = -ENOMEM; + goto err_device_init; } bus_dev = &bus_node->dev; device_initialize(bus_dev); @@ -983,47 +981,37 @@ static struct device *msm_bus_device_init( node_info = devm_kzalloc(bus_dev, sizeof(struct msm_bus_node_info_type), GFP_KERNEL); if (!node_info) { - MSM_BUS_ERR("%s:Bus node info alloc failed\n", __func__); - devm_kfree(bus_dev, bus_node); - kfree(bus_dev); - bus_dev = NULL; - goto exit_device_init; + ret = -ENOMEM; + goto err_put_device; } bus_node->node_info = node_info; bus_node->ap_owned = pdata->ap_owned; bus_dev->of_node = pdata->of_node; - if (msm_bus_copy_node_info(pdata, bus_dev) < 0) { - devm_kfree(bus_dev, bus_node); - devm_kfree(bus_dev, node_info); - kfree(bus_dev); - bus_dev = NULL; - goto exit_device_init; - } + ret = msm_bus_copy_node_info(pdata, bus_dev); + if (ret) + goto err_put_device; bus_dev->bus = &msm_bus_type; dev_set_name(bus_dev, bus_node->node_info->name); ret = device_add(bus_dev); - if (ret < 0) { + if (ret) { MSM_BUS_ERR("%s: Error registering device %d", __func__, pdata->node_info->id); - devm_kfree(bus_dev, bus_node); - devm_kfree(bus_dev, node_info->dev_connections); - devm_kfree(bus_dev, node_info->connections); - devm_kfree(bus_dev, node_info->black_connections); - devm_kfree(bus_dev, node_info->black_listed_connections); - devm_kfree(bus_dev, node_info); - kfree(bus_dev); - bus_dev = NULL; - goto exit_device_init; + goto err_put_device; } device_create_file(bus_dev, &dev_attr_bw); INIT_LIST_HEAD(&bus_node->devlist); - -exit_device_init: return bus_dev; + +err_put_device: + put_device(bus_dev); + bus_dev = NULL; + kfree(bus_node); +err_device_init: + return ERR_PTR(ret); } static int msm_bus_setup_dev_conn(struct device *bus_dev, void *data) @@ -1170,10 +1158,10 @@ static int msm_bus_device_probe(struct platform_device *pdev) node_dev = msm_bus_device_init(&pdata->info[i]); - if (!node_dev) { + if (IS_ERR(node_dev)) { MSM_BUS_ERR("%s: Error during dev init for %d", __func__, pdata->info[i].node_info->id); - ret = -ENXIO; + ret = PTR_ERR(node_dev); goto exit_device_probe; } diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c index d82c36480159..77fd08225487 100644 --- a/drivers/soc/qcom/peripheral-loader.c +++ b/drivers/soc/qcom/peripheral-loader.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-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 @@ -147,7 +147,8 @@ struct pil_priv { phys_addr_t region_end; void *region; struct pil_image_info __iomem *info; - struct md_ssr_ss_info __iomem *minidump; + struct md_ssr_ss_info __iomem *minidump_ss; + struct md_ssr_ss_info __iomem *minidump_pdr; int minidump_id; int id; int unvoted_flag; @@ -156,30 +157,54 @@ struct pil_priv { static int pil_do_minidump(struct pil_desc *desc, void *ramdump_dev) { - struct boot_minidump_smem_region __iomem *region_info; + struct boot_minidump_smem_region __iomem *region_info_ss; + struct boot_minidump_smem_region __iomem *region_info_pdr; struct ramdump_segment *ramdump_segs, *s; struct pil_priv *priv = desc->priv; - void __iomem *subsys_smem_base; - void __iomem *offset; - int ss_mdump_seg_cnt; + void __iomem *subsys_smem_base_pdr; + void __iomem *subsys_smem_base_ss; + void __iomem *offset_ss; + void __iomem *offset_pdr; + int ss_mdump_seg_cnt_ss = 0, ss_mdump_seg_cnt_pdr = 0, total_segs; int ret, i; if (!ramdump_dev) return -ENODEV; - memcpy(&offset, &priv->minidump, sizeof(priv->minidump)); - offset = offset + sizeof(priv->minidump->md_ss_smem_regions_baseptr); + memcpy(&offset_ss, &priv->minidump_ss, sizeof(priv->minidump_ss)); + offset_ss = offset_ss + + sizeof(priv->minidump_ss->md_ss_smem_regions_baseptr); /* There are 3 encryption keys which also need to be dumped */ - ss_mdump_seg_cnt = readb_relaxed(offset) + + ss_mdump_seg_cnt_ss = readb_relaxed(offset_ss) + NUM_OF_ENCRYPTED_KEY; - pr_debug("SMEM base to read minidump segments is 0x%x\n", - __raw_readl(priv->minidump)); - subsys_smem_base = ioremap(__raw_readl(priv->minidump), - ss_mdump_seg_cnt * sizeof(*region_info)); - region_info = - (struct boot_minidump_smem_region __iomem *)subsys_smem_base; - ramdump_segs = kcalloc(ss_mdump_seg_cnt, + pr_debug("SMEM base to read minidump ss segments is 0x%x\n", + __raw_readl(priv->minidump_ss)); + subsys_smem_base_ss = ioremap(__raw_readl(priv->minidump_ss), + ss_mdump_seg_cnt_ss * sizeof(*region_info_ss)); + region_info_ss = + (struct boot_minidump_smem_region __iomem *)subsys_smem_base_ss; + + if (priv->minidump_pdr && (__raw_readl(priv->minidump_pdr) != 0)) { + memcpy(&offset_pdr, &priv->minidump_pdr, + sizeof(priv->minidump_pdr)); + offset_pdr = offset_pdr + + sizeof(priv->minidump_pdr->md_ss_smem_regions_baseptr); + /* There are 3 encryption keys which also need to be dumped */ + ss_mdump_seg_cnt_pdr = readb_relaxed(offset_pdr) + + NUM_OF_ENCRYPTED_KEY; + + pr_debug("SMEM base to read minidump pdr segments is 0x%x\n", + __raw_readl(priv->minidump_pdr)); + subsys_smem_base_pdr = ioremap(__raw_readl(priv->minidump_pdr), + ss_mdump_seg_cnt_pdr * sizeof(*region_info_pdr)); + region_info_pdr = + (struct boot_minidump_smem_region __iomem *) + subsys_smem_base_pdr; + } + + total_segs = ss_mdump_seg_cnt_ss + ss_mdump_seg_cnt_pdr; + ramdump_segs = kcalloc(total_segs, sizeof(*ramdump_segs), GFP_KERNEL); if (!ramdump_segs) return -ENOMEM; @@ -189,25 +214,43 @@ static int pil_do_minidump(struct pil_desc *desc, void *ramdump_dev) (priv->region_end - priv->region_start)); s = ramdump_segs; - for (i = 0; i < ss_mdump_seg_cnt; i++) { - memcpy(&offset, ®ion_info, sizeof(region_info)); - memcpy(&s->name, ®ion_info, sizeof(region_info)); - offset = offset + sizeof(region_info->region_name); - s->address = __raw_readl(offset); - offset = offset + sizeof(region_info->region_base_address); - s->size = __raw_readl(offset); + for (i = 0; i < ss_mdump_seg_cnt_ss; i++) { + memcpy(&offset_ss, ®ion_info_ss, sizeof(region_info_ss)); + memcpy(&s->name, ®ion_info_ss, sizeof(region_info_ss)); + offset_ss = offset_ss + sizeof(region_info_ss->region_name); + s->address = __raw_readl(offset_ss); + offset_ss = offset_ss + + sizeof(region_info_ss->region_base_address); + s->size = __raw_readl(offset_ss); + pr_debug("Dumping segment %s with address %pK and size 0x%x\n", + s->name, (void *)s->address, + (unsigned int)s->size); + s++; + region_info_ss++; + } + + for (i = 0; i < ss_mdump_seg_cnt_pdr; i++) { + memcpy(&offset_pdr, ®ion_info_pdr, sizeof(region_info_pdr)); + memcpy(&s->name, ®ion_info_pdr, sizeof(region_info_pdr)); + offset_pdr = offset_pdr + sizeof(region_info_pdr->region_name); + s->address = __raw_readl(offset_pdr); + offset_pdr = offset_pdr + + sizeof(region_info_pdr->region_base_address); + s->size = __raw_readl(offset_pdr); pr_debug("Dumping segment %s with address %pK and size 0x%x\n", s->name, (void *)s->address, (unsigned int)s->size); s++; - region_info++; + region_info_pdr++; } - ret = do_minidump(ramdump_dev, ramdump_segs, ss_mdump_seg_cnt); + + ret = do_minidump(ramdump_dev, ramdump_segs, total_segs); kfree(ramdump_segs); if (ret) pil_err(desc, "%s: Ramdump collection failed for subsys %s rc:%d\n", __func__, desc->name, ret); - writeb_relaxed(1, &priv->minidump->md_ss_ssr_cause); + writeb_relaxed(1, &priv->minidump_ss->md_ss_ssr_cause); + writeb_relaxed(1, &priv->minidump_pdr->md_ss_ssr_cause); if (desc->subsys_vmid > 0) ret = pil_assign_mem_to_subsys(desc, priv->region_start, @@ -232,13 +275,13 @@ int pil_do_ramdump(struct pil_desc *desc, struct ramdump_segment *ramdump_segs, *s; void __iomem *offset; - memcpy(&offset, &priv->minidump, sizeof(priv->minidump)); + memcpy(&offset, &priv->minidump_ss, sizeof(priv->minidump_ss)); /* * Collect minidump if smem base is initialized, * ssr cause is 0. No need to check encryption status */ - if (priv->minidump - && (__raw_readl(priv->minidump) != 0) + if (priv->minidump_ss + && (__raw_readl(priv->minidump_ss) != 0) && (readb_relaxed(offset + sizeof(u32) + 2 * sizeof(u8)) == 0)) { pr_debug("Dumping Minidump for %s\n", desc->name); return pil_do_minidump(desc, minidump_dev); @@ -1107,7 +1150,7 @@ int pil_desc_init(struct pil_desc *desc) { struct pil_priv *priv; void __iomem *addr; - int ret, ss_imem_offset_mdump; + int ret, ss_imem_offset_mdump_ss, ss_imem_offset_mdump_pdr; char buf[sizeof(priv->info->name)]; struct device_node *ofnode = desc->dev->of_node; @@ -1136,16 +1179,25 @@ int pil_desc_init(struct pil_desc *desc) &priv->minidump_id)) pr_debug("minidump-id not found for %s\n", desc->name); else { - ss_imem_offset_mdump = + ss_imem_offset_mdump_ss = sizeof(struct md_ssr_ss_info) * priv->minidump_id; + ss_imem_offset_mdump_pdr = + sizeof(struct md_ssr_ss_info) * (priv->minidump_id + 1); if (pil_minidump_base) { /* Add 0x4 to get start of struct md_ssr_ss_info base * from struct md_ssr_toc for any subsystem, * struct md_ssr_ss_info is actually the pointer * of ToC in smem for any subsystem. */ - addr = pil_minidump_base + ss_imem_offset_mdump + 0x4; - priv->minidump = (struct md_ssr_ss_info __iomem *)addr; + addr = pil_minidump_base + + ss_imem_offset_mdump_ss + 0x4; + priv->minidump_ss = + (struct md_ssr_ss_info __iomem *)addr; + + addr = pil_minidump_base + + ss_imem_offset_mdump_pdr + 0x4; + priv->minidump_pdr = + (struct md_ssr_ss_info __iomem *)addr; } } diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c index fc4220cb6765..aef660104c5f 100644 --- a/drivers/soc/qcom/pil-msa.c +++ b/drivers/soc/qcom/pil-msa.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 @@ -79,7 +79,7 @@ /* CX_IPEAK Parameters */ #define CX_IPEAK_MSS BIT(5) /* Timeout value for MBA boot when minidump is enabled */ -#define MBA_ENCRYPTION_TIMEOUT 3000 +#define MBA_ENCRYPTION_TIMEOUT 5000 enum scm_cmd { PAS_MEM_SETUP_CMD = 2, }; diff --git a/drivers/soc/qcom/qdsp6v2/voice_svc.c b/drivers/soc/qcom/qdsp6v2/voice_svc.c index fe5458974406..f01ab2499a75 100644 --- a/drivers/soc/qcom/qdsp6v2/voice_svc.c +++ b/drivers/soc/qcom/qdsp6v2/voice_svc.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 @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -67,7 +68,11 @@ static void *dummy_q6_mvm; static void *dummy_q6_cvs; dev_t device_num; +static spinlock_t voicesvc_lock; +static bool is_released; static int voice_svc_dummy_reg(void); +static int voice_svc_dummy_dereg(void); + static int32_t qdsp_dummy_apr_callback(struct apr_client_data *data, void *priv); @@ -82,10 +87,16 @@ static int32_t qdsp_apr_callback(struct apr_client_data *data, void *priv) return -EINVAL; } + spin_lock(&voicesvc_lock); + if (is_released) { + spin_unlock(&voicesvc_lock); + return 0; + } prtd = (struct voice_svc_prvt *)priv; if (prtd == NULL) { pr_err("%s: private data is NULL\n", __func__); + spin_unlock(&voicesvc_lock); return -EINVAL; } @@ -128,6 +139,7 @@ static int32_t qdsp_apr_callback(struct apr_client_data *data, void *priv) spin_unlock_irqrestore(&prtd->response_lock, spin_flags); + spin_unlock(&voicesvc_lock); return -ENOMEM; } @@ -156,6 +168,7 @@ static int32_t qdsp_apr_callback(struct apr_client_data *data, void *priv) __func__); } + spin_unlock(&voicesvc_lock); return 0; } @@ -614,6 +627,21 @@ static int voice_svc_dummy_reg() return -EINVAL; } +static int voice_svc_dummy_dereg(void) +{ + pr_debug("%s\n", __func__); + if (dummy_q6_mvm != NULL) { + apr_deregister(dummy_q6_mvm); + dummy_q6_mvm = NULL; + } + + if (dummy_q6_cvs != NULL) { + apr_deregister(dummy_q6_cvs); + dummy_q6_cvs = NULL; + } + return 0; +} + static int voice_svc_open(struct inode *inode, struct file *file) { struct voice_svc_prvt *prtd = NULL; @@ -637,6 +665,7 @@ static int voice_svc_open(struct inode *inode, struct file *file) mutex_init(&prtd->response_mutex_lock); file->private_data = (void *)prtd; + is_released = 0; /* Current APR implementation doesn't support session based * multiple service registrations. The apr_deregister() * function sets the destination and client IDs to zero, if @@ -669,6 +698,11 @@ static int voice_svc_release(struct inode *inode, struct file *file) goto done; } + mutex_lock(&prtd->response_mutex_lock); + if (reg_dummy_sess) { + voice_svc_dummy_dereg(); + reg_dummy_sess = 0; + } if (prtd->apr_q6_cvs != NULL) { svc_name = VOICE_SVC_MVM_STR; handle = &prtd->apr_q6_cvs; @@ -685,7 +719,6 @@ static int voice_svc_release(struct inode *inode, struct file *file) pr_err("%s: Failed to dereg MVM %d\n", __func__, ret); } - mutex_lock(&prtd->response_mutex_lock); spin_lock_irqsave(&prtd->response_lock, spin_flags); while (!list_empty(&prtd->response_queue)) { @@ -703,9 +736,11 @@ static int voice_svc_release(struct inode *inode, struct file *file) mutex_destroy(&prtd->response_mutex_lock); + spin_lock(&voicesvc_lock); kfree(file->private_data); file->private_data = NULL; - + is_released = 1; + spin_unlock(&voicesvc_lock); done: return ret; } @@ -738,7 +773,7 @@ static int voice_svc_probe(struct platform_device *pdev) if (ret) { pr_err("%s: Failed to alloc chrdev\n", __func__); ret = -ENODEV; - goto chrdev_err; + goto done; } voice_svc_dev->major = MAJOR(device_num); @@ -774,6 +809,7 @@ static int voice_svc_probe(struct platform_device *pdev) goto add_err; } pr_debug("%s: Device created\n", __func__); + spin_lock_init(&voicesvc_lock); goto done; add_err: @@ -784,8 +820,6 @@ static int voice_svc_probe(struct platform_device *pdev) class_destroy(voice_svc_class); class_err: unregister_chrdev_region(0, MINOR_NUMBER); -chrdev_err: - kfree(voice_svc_dev); done: return ret; } @@ -799,7 +833,6 @@ static int voice_svc_remove(struct platform_device *pdev) device_destroy(voice_svc_class, device_num); class_destroy(voice_svc_class); unregister_chrdev_region(0, MINOR_NUMBER); - kfree(voice_svc_dev); return 0; } diff --git a/drivers/soc/qcom/qpnp-haptic.c b/drivers/soc/qcom/qpnp-haptic.c index f6ca5175b6f8..d9d8e6e8b13d 100644 --- a/drivers/soc/qcom/qpnp-haptic.c +++ b/drivers/soc/qcom/qpnp-haptic.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2015, 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 @@ -2232,6 +2232,8 @@ static void qpnp_hap_td_enable(struct timed_output_dev *dev, int time_ms) { struct qpnp_hap *hap = container_of(dev, struct qpnp_hap, timed_dev); + bool state = !!time_ms; + ktime_t rem; int rc; if (time_ms < 0) @@ -2239,38 +2241,49 @@ static void qpnp_hap_td_enable(struct timed_output_dev *dev, int time_ms) mutex_lock(&hap->lock); - if (time_ms == 0) { - /* disable haptics */ - hrtimer_cancel(&hap->hap_timer); - hap->state = 0; - schedule_work(&hap->work); + if (hap->state == state) { + if (state) { + rem = hrtimer_get_remaining(&hap->hap_timer); + if (time_ms > ktime_to_ms(rem)) { + time_ms = (time_ms > hap->timeout_ms ? + hap->timeout_ms : time_ms); + hrtimer_cancel(&hap->hap_timer); + hap->play_time_ms = time_ms; + hrtimer_start(&hap->hap_timer, + ktime_set(time_ms / 1000, + (time_ms % 1000) * 1000000), + HRTIMER_MODE_REL); + } + } mutex_unlock(&hap->lock); return; } - if (time_ms < 10) - time_ms = 10; - - if (is_sw_lra_auto_resonance_control(hap)) - hrtimer_cancel(&hap->auto_res_err_poll_timer); - - hrtimer_cancel(&hap->hap_timer); + hap->state = state; + if (!hap->state) { + hrtimer_cancel(&hap->hap_timer); + } else { + if (time_ms < 10) + time_ms = 10; - if (hap->auto_mode) { - rc = qpnp_hap_auto_mode_config(hap, time_ms); - if (rc < 0) { - pr_err("Unable to do auto mode config\n"); - mutex_unlock(&hap->lock); - return; + if (hap->auto_mode) { + rc = qpnp_hap_auto_mode_config(hap, time_ms); + if (rc < 0) { + pr_err("Unable to do auto mode config\n"); + mutex_unlock(&hap->lock); + return; + } } + + time_ms = (time_ms > hap->timeout_ms ? + hap->timeout_ms : time_ms); + hap->play_time_ms = time_ms; + hrtimer_start(&hap->hap_timer, + ktime_set(time_ms / 1000, + (time_ms % 1000) * 1000000), + HRTIMER_MODE_REL); } - time_ms = (time_ms > hap->timeout_ms ? hap->timeout_ms : time_ms); - hap->play_time_ms = time_ms; - hap->state = 1; - hrtimer_start(&hap->hap_timer, - ktime_set(time_ms / 1000, (time_ms % 1000) * 1000000), - HRTIMER_MODE_REL); mutex_unlock(&hap->lock); schedule_work(&hap->work); } diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c index f19db5fe99b3..52355699e4f3 100644 --- a/drivers/soc/qcom/service-locator.c +++ b/drivers/soc/qcom/service-locator.c @@ -149,11 +149,10 @@ static void service_locator_recv_msg(struct work_struct *work) do { pr_debug("Notified about a Receive event\n"); - ret = qmi_recv_msg(service_locator.clnt_handle); - if (ret < 0) - pr_err("Error receiving message rc:%d. Retrying...\n", - ret); - } while (ret == 0); + } while ((ret = qmi_recv_msg(service_locator.clnt_handle)) == 0); + + if (ret != -ENOMSG) + pr_err("Error receiving message rc:%d\n", ret); } @@ -190,7 +189,7 @@ static int servreg_loc_send_msg(struct msg_desc *req_desc, */ rc = qmi_send_req_wait(service_locator.clnt_handle, req_desc, req, sizeof(*req), resp_desc, resp, sizeof(*resp), - msecs_to_jiffies(QMI_SERVREG_LOC_SERVER_TIMEOUT)); + QMI_SERVREG_LOC_SERVER_TIMEOUT); if (rc < 0) { pr_err("QMI send req failed for client %s, ret - %d\n", pd->client_name, rc); diff --git a/drivers/staging/android/ion/ion_cma_secure_heap.c b/drivers/staging/android/ion/ion_cma_secure_heap.c index 90ae7eb65b65..6102b1765182 100644 --- a/drivers/staging/android/ion/ion_cma_secure_heap.c +++ b/drivers/staging/android/ion/ion_cma_secure_heap.c @@ -3,7 +3,7 @@ * * Copyright (C) Linaro 2012 * Author: for ST-Ericsson. - * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -344,7 +344,8 @@ static void ion_secure_cma_free_chunk(struct ion_cma_secure_heap *sheap, } -void __ion_secure_cma_shrink_pool(struct ion_cma_secure_heap *sheap, int max_nr) +static unsigned long +__ion_secure_cma_shrink_pool(struct ion_cma_secure_heap *sheap, int max_nr) { struct list_head *entry, *_n; unsigned long drained_size = 0, skipped_size = 0; @@ -368,6 +369,7 @@ void __ion_secure_cma_shrink_pool(struct ion_cma_secure_heap *sheap, int max_nr) } trace_ion_secure_cma_shrink_pool_end(drained_size, skipped_size); + return drained_size; } int ion_secure_cma_drain_pool(struct ion_heap *heap, void *unused) @@ -385,6 +387,7 @@ int ion_secure_cma_drain_pool(struct ion_heap *heap, void *unused) static unsigned long ion_secure_cma_shrinker(struct shrinker *shrinker, struct shrink_control *sc) { + unsigned long freed; struct ion_cma_secure_heap *sheap = container_of(shrinker, struct ion_cma_secure_heap, shrinker); int nr_to_scan = sc->nr_to_scan; @@ -397,11 +400,11 @@ static unsigned long ion_secure_cma_shrinker(struct shrinker *shrinker, if (!mutex_trylock(&sheap->chunk_lock)) return -1; - __ion_secure_cma_shrink_pool(sheap, nr_to_scan); + freed = __ion_secure_cma_shrink_pool(sheap, nr_to_scan); mutex_unlock(&sheap->chunk_lock); - return atomic_read(&sheap->total_pool_size); + return freed; } static unsigned long ion_secure_cma_shrinker_count(struct shrinker *shrinker, diff --git a/drivers/staging/android/ion/ion_page_pool.c b/drivers/staging/android/ion/ion_page_pool.c index 513d015a5ace..0034dfe17ac8 100644 --- a/drivers/staging/android/ion/ion_page_pool.c +++ b/drivers/staging/android/ion/ion_page_pool.c @@ -64,6 +64,9 @@ static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page) list_add_tail(&page->lru, &pool->low_items); pool->low_count++; } + + mod_zone_page_state(page_zone(page), NR_INDIRECTLY_RECLAIMABLE_BYTES, + (1 << (PAGE_SHIFT + pool->order))); mutex_unlock(&pool->mutex); return 0; } @@ -83,6 +86,8 @@ static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high) } list_del(&page->lru); + mod_zone_page_state(page_zone(page), NR_INDIRECTLY_RECLAIMABLE_BYTES, + -(1 << (PAGE_SHIFT + pool->order))); return page; } @@ -183,7 +188,7 @@ int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask, freed += (1 << pool->order); } - return ion_page_pool_total(pool, high); + return freed; } struct ion_page_pool *ion_page_pool_create(struct device *dev, gfp_t gfp_mask, diff --git a/drivers/staging/android/ion/ion_system_heap.c b/drivers/staging/android/ion/ion_system_heap.c index 2ad4cc7a4785..a2ead280ac4e 100644 --- a/drivers/staging/android/ion/ion_system_heap.c +++ b/drivers/staging/android/ion/ion_system_heap.c @@ -2,7 +2,7 @@ * drivers/staging/android/ion/ion_system_heap.c * * Copyright (C) 2011 Google, Inc. - * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2018, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -754,8 +754,10 @@ static void ion_system_heap_destroy_pools(struct ion_page_pool **pools) { int i; for (i = 0; i < num_orders; i++) - if (pools[i]) + if (pools[i]) { ion_page_pool_destroy(pools[i]); + pools[i] = NULL; + } } /** diff --git a/drivers/staging/android/ion/msm/msm_ion.c b/drivers/staging/android/ion/msm/msm_ion.c index a1dd5ccc8109..9b38b73a6c0f 100644 --- a/drivers/staging/android/ion/msm/msm_ion.c +++ b/drivers/staging/android/ion/msm/msm_ion.c @@ -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 @@ -742,7 +742,7 @@ long msm_ion_custom_ioctl(struct ion_client *client, data.flush_data.offset; end = start + data.flush_data.length; - if (check_vaddr_bounds(start, end)) { + if (start && check_vaddr_bounds(start, end)) { pr_err("%s: virtual address %pK is out of bounds\n", __func__, data.flush_data.vaddr); ret = -EINVAL; diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index b1298f093f13..cd96f470d562 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -609,15 +609,23 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx, } else if (header->bDescriptorType == USB_DT_INTERFACE_ASSOCIATION) { + struct usb_interface_assoc_descriptor *d; + + d = (struct usb_interface_assoc_descriptor *)header; + if (d->bLength < USB_DT_INTERFACE_ASSOCIATION_SIZE) { + dev_warn(ddev, + "config %d has an invalid interface association descriptor of length %d, skipping\n", + cfgno, d->bLength); + continue; + } + if (iad_num == USB_MAXIADS) { dev_warn(ddev, "found more Interface " "Association Descriptors " "than allocated for in " "configuration %d\n", cfgno); } else { - config->intf_assoc[iad_num] = - (struct usb_interface_assoc_descriptor - *)header; + config->intf_assoc[iad_num] = d; iad_num++; } @@ -722,18 +730,21 @@ void usb_destroy_configuration(struct usb_device *dev) return; if (dev->rawdescriptors) { - for (i = 0; i < dev->descriptor.bNumConfigurations; i++) + for (i = 0; i < dev->descriptor.bNumConfigurations && + i < USB_MAXCONFIG; i++) kfree(dev->rawdescriptors[i]); kfree(dev->rawdescriptors); dev->rawdescriptors = NULL; } - for (c = 0; c < dev->descriptor.bNumConfigurations; c++) { + for (c = 0; c < dev->descriptor.bNumConfigurations && + c < USB_MAXCONFIG; c++) { struct usb_host_config *cf = &dev->config[c]; kfree(cf->string); - for (i = 0; i < cf->desc.bNumInterfaces; i++) { + for (i = 0; i < cf->desc.bNumInterfaces && + i < USB_MAXINTERFACES; i++) { if (cf->intf_cache[i]) kref_put(&cf->intf_cache[i]->ref, usb_release_interface_cache); @@ -918,10 +929,12 @@ int usb_get_bos_descriptor(struct usb_device *dev) for (i = 0; i < num; i++) { buffer += length; cap = (struct usb_dev_cap_header *)buffer; - length = cap->bLength; - if (total_len < length) + if (total_len < sizeof(*cap) || total_len < cap->bLength) { + dev->bos->desc->bNumDeviceCaps = i; break; + } + length = cap->bLength; total_len -= length; if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) { diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 8e641b5893ed..70ce58fd2cb0 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include /* for usbcore internals */ #include @@ -2027,3 +2028,159 @@ int usb_driver_set_configuration(struct usb_device *udev, int config) return 0; } EXPORT_SYMBOL_GPL(usb_driver_set_configuration); + +/** + * cdc_parse_cdc_header - parse the extra headers present in CDC devices + * @hdr: the place to put the results of the parsing + * @intf: the interface for which parsing is requested + * @buffer: pointer to the extra headers to be parsed + * @buflen: length of the extra headers + * + * This evaluates the extra headers present in CDC devices which + * bind the interfaces for data and control and provide details + * about the capabilities of the device. + * + * Return: number of descriptors parsed or -EINVAL + * if the header is contradictory beyond salvage + */ + +int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr, + struct usb_interface *intf, + u8 *buffer, + int buflen) +{ + /* duplicates are ignored */ + struct usb_cdc_union_desc *union_header = NULL; + + /* duplicates are not tolerated */ + struct usb_cdc_header_desc *header = NULL; + struct usb_cdc_ether_desc *ether = NULL; + struct usb_cdc_mdlm_detail_desc *detail = NULL; + struct usb_cdc_mdlm_desc *desc = NULL; + + unsigned int elength; + int cnt = 0; + + memset(hdr, 0x00, sizeof(struct usb_cdc_parsed_header)); + hdr->phonet_magic_present = false; + while (buflen > 0) { + elength = buffer[0]; + if (!elength) { + dev_err(&intf->dev, "skipping garbage byte\n"); + elength = 1; + goto next_desc; + } + if ((buflen < elength) || (elength < 3)) { + dev_err(&intf->dev, "invalid descriptor buffer length\n"); + break; + } + if (buffer[1] != USB_DT_CS_INTERFACE) { + dev_err(&intf->dev, "skipping garbage\n"); + goto next_desc; + } + + switch (buffer[2]) { + case USB_CDC_UNION_TYPE: /* we've found it */ + if (elength < sizeof(struct usb_cdc_union_desc)) + goto next_desc; + if (union_header) { + dev_err(&intf->dev, "More than one union descriptor, skipping ...\n"); + goto next_desc; + } + union_header = (struct usb_cdc_union_desc *)buffer; + break; + case USB_CDC_COUNTRY_TYPE: + if (elength < sizeof(struct usb_cdc_country_functional_desc)) + goto next_desc; + hdr->usb_cdc_country_functional_desc = + (struct usb_cdc_country_functional_desc *)buffer; + break; + case USB_CDC_HEADER_TYPE: + if (elength != sizeof(struct usb_cdc_header_desc)) + goto next_desc; + if (header) + return -EINVAL; + header = (struct usb_cdc_header_desc *)buffer; + break; + case USB_CDC_ACM_TYPE: + if (elength < sizeof(struct usb_cdc_acm_descriptor)) + goto next_desc; + hdr->usb_cdc_acm_descriptor = + (struct usb_cdc_acm_descriptor *)buffer; + break; + case USB_CDC_ETHERNET_TYPE: + if (elength != sizeof(struct usb_cdc_ether_desc)) + goto next_desc; + if (ether) + return -EINVAL; + ether = (struct usb_cdc_ether_desc *)buffer; + break; + case USB_CDC_CALL_MANAGEMENT_TYPE: + if (elength < sizeof(struct usb_cdc_call_mgmt_descriptor)) + goto next_desc; + hdr->usb_cdc_call_mgmt_descriptor = + (struct usb_cdc_call_mgmt_descriptor *)buffer; + break; + case USB_CDC_DMM_TYPE: + if (elength < sizeof(struct usb_cdc_dmm_desc)) + goto next_desc; + hdr->usb_cdc_dmm_desc = + (struct usb_cdc_dmm_desc *)buffer; + break; + case USB_CDC_MDLM_TYPE: + if (elength < sizeof(struct usb_cdc_mdlm_desc *)) + goto next_desc; + if (desc) + return -EINVAL; + desc = (struct usb_cdc_mdlm_desc *)buffer; + break; + case USB_CDC_MDLM_DETAIL_TYPE: + if (elength < sizeof(struct usb_cdc_mdlm_detail_desc *)) + goto next_desc; + if (detail) + return -EINVAL; + detail = (struct usb_cdc_mdlm_detail_desc *)buffer; + break; + case USB_CDC_NCM_TYPE: + if (elength < sizeof(struct usb_cdc_ncm_desc)) + goto next_desc; + hdr->usb_cdc_ncm_desc = (struct usb_cdc_ncm_desc *)buffer; + break; + case USB_CDC_MBIM_TYPE: + if (elength < sizeof(struct usb_cdc_mbim_desc)) + goto next_desc; + + hdr->usb_cdc_mbim_desc = (struct usb_cdc_mbim_desc *)buffer; + break; + case USB_CDC_MBIM_EXTENDED_TYPE: + if (elength < sizeof(struct usb_cdc_mbim_extended_desc)) + break; + hdr->usb_cdc_mbim_extended_desc = + (struct usb_cdc_mbim_extended_desc *)buffer; + break; + case CDC_PHONET_MAGIC_NUMBER: + hdr->phonet_magic_present = true; + break; + default: + /* + * there are LOTS more CDC descriptors that + * could legitimately be found here. + */ + dev_dbg(&intf->dev, "Ignoring descriptor: type %02x, length %ud\n", + buffer[2], elength); + goto next_desc; + } + cnt++; +next_desc: + buflen -= elength; + buffer += elength; + } + hdr->usb_cdc_union_desc = union_header; + hdr->usb_cdc_header_desc = header; + hdr->usb_cdc_mdlm_detail_desc = detail; + hdr->usb_cdc_mdlm_desc = desc; + hdr->usb_cdc_ether_desc = ether; + return cnt; +} + +EXPORT_SYMBOL(cdc_parse_cdc_header); diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 7d99243af545..cf15716c4d19 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.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 @@ -234,6 +234,7 @@ struct dwc3_msm { struct pm_qos_request pm_qos_req_dma; struct delayed_work perf_vote_work; struct delayed_work sdp_check; + bool usb_compliance_mode; struct mutex suspend_resume_mutex; }; @@ -2652,6 +2653,13 @@ static void check_for_sdp_connection(struct work_struct *w) if (!mdwc->vbus_active) return; + /* USB 3.1 compliance equipment usually repoted as floating + * charger as HS dp/dm lines are never connected. Do not + * tear down USB stack if compliance parameter is set + */ + if (mdwc->usb_compliance_mode) + return; + /* floating D+/D- lines detected */ if (dwc->gadget.state < USB_STATE_DEFAULT && dwc3_gadget_get_link_state(dwc) != DWC3_LINK_STATE_CMPLY) { @@ -2861,6 +2869,30 @@ static ssize_t xhci_link_compliance_store(struct device *dev, static DEVICE_ATTR_RW(xhci_link_compliance); +static ssize_t usb_compliance_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dwc3_msm *mdwc = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%c\n", + mdwc->usb_compliance_mode ? 'Y' : 'N'); +} + +static ssize_t usb_compliance_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + struct dwc3_msm *mdwc = dev_get_drvdata(dev); + + ret = strtobool(buf, &mdwc->usb_compliance_mode); + + if (ret) + return ret; + + return count; +} +static DEVICE_ATTR_RW(usb_compliance_mode); + static int dwc3_msm_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node, *dwc3_node; @@ -3207,6 +3239,7 @@ static int dwc3_msm_probe(struct platform_device *pdev) device_create_file(&pdev->dev, &dev_attr_mode); device_create_file(&pdev->dev, &dev_attr_speed); device_create_file(&pdev->dev, &dev_attr_xhci_link_compliance); + device_create_file(&pdev->dev, &dev_attr_usb_compliance_mode); host_mode = usb_get_dr_mode(&mdwc->dwc3->dev) == USB_DR_MODE_HOST; if (host_mode || @@ -3446,6 +3479,7 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on) if (on) { dev_dbg(mdwc->dev, "%s: turn on host\n", __func__); + pm_runtime_get_sync(mdwc->dev); mdwc->hs_phy->flags |= PHY_HOST_MODE; if (dwc->maximum_speed == USB_SPEED_SUPER) { mdwc->ss_phy->flags |= PHY_HOST_MODE; @@ -3454,7 +3488,6 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on) } usb_phy_notify_connect(mdwc->hs_phy, USB_SPEED_HIGH); - pm_runtime_get_sync(mdwc->dev); dbg_event(0xFF, "StrtHost gync", atomic_read(&mdwc->dev->power.usage_count)); if (!IS_ERR(mdwc->vbus_reg)) diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c index 7950f25136a5..fc1979e09a03 100644 --- a/drivers/usb/gadget/function/f_accessory.c +++ b/drivers/usb/gadget/function/f_accessory.c @@ -879,6 +879,12 @@ int acc_ctrlrequest(struct usb_composite_dev *cdev, u16 w_length = le16_to_cpu(ctrl->wLength); unsigned long flags; + /* + * If instance is not created which is the case in power off charging + * mode, dev will be NULL. Hence return error if it is the case. + */ + if (!dev) + return -ENODEV; /* * printk(KERN_INFO "acc_ctrlrequest " * "%02x.%02x v%04x i%04x l%u\n", diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c index ec73b67096c5..7d8bfe62b148 100644 --- a/drivers/usb/gadget/function/f_audio_source.c +++ b/drivers/usb/gadget/function/f_audio_source.c @@ -591,14 +591,38 @@ static int audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) pr_debug("audio_set_alt intf %d, alt %d\n", intf, alt); + if (!alt) { + usb_ep_disable(audio->in_ep); + return 0; + } + ret = config_ep_by_speed(cdev->gadget, f, audio->in_ep); - if (ret) + if (ret) { + audio->in_ep->desc = NULL; + pr_err("config_ep fail for audio ep ret %d\n", ret); + return ret; + } + ret = usb_ep_enable(audio->in_ep); + if (ret) { + audio->in_ep->desc = NULL; + pr_err("failed to enable audio ret %d\n", ret); return ret; + } - usb_ep_enable(audio->in_ep); return 0; } +/* + * Because the data interface supports multiple altsettings, + * this audio_source function *MUST* implement a get_alt() method. + */ +static int audio_get_alt(struct usb_function *f, unsigned int intf) +{ + struct audio_dev *audio = func_to_audio(f); + + return audio->in_ep->enabled ? 1 : 0; +} + static void audio_disable(struct usb_function *f) { struct audio_dev *audio = func_to_audio(f); @@ -862,6 +886,7 @@ static struct audio_dev _audio_dev = { .bind = audio_bind, .unbind = audio_unbind, .set_alt = audio_set_alt, + .get_alt = audio_get_alt, .setup = audio_setup, .disable = audio_disable, .free_func = audio_free_func, diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index c001dcda62fd..792d35755587 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -49,6 +49,7 @@ static struct gsi_inst_status { /* Deregister misc device and free instance structures */ static void gsi_inst_clean(struct gsi_opts *opts); +static void gsi_rndis_ipa_reset_trigger(struct gsi_data_port *d_port); static void ipa_disconnect_handler(struct gsi_data_port *d_port); static int gsi_ctrl_send_notification(struct f_gsi *gsi); static int gsi_alloc_trb_buffer(struct f_gsi *gsi); @@ -501,14 +502,11 @@ static void ipa_disconnect_handler(struct gsi_data_port *d_port) */ usb_gsi_ep_op(d_port->in_ep, (void *)&block_db, GSI_EP_OP_SET_CLR_BLOCK_DBL); - gsi->in_ep_desc_backup = gsi->d_port.in_ep->desc; usb_gsi_ep_op(gsi->d_port.in_ep, NULL, GSI_EP_OP_DISABLE); } - if (gsi->d_port.out_ep) { - gsi->out_ep_desc_backup = gsi->d_port.out_ep->desc; + if (gsi->d_port.out_ep) usb_gsi_ep_op(gsi->d_port.out_ep, NULL, GSI_EP_OP_DISABLE); - } gsi->d_port.net_ready_trigger = false; } @@ -614,6 +612,7 @@ static void ipa_work_handler(struct work_struct *w) struct usb_gadget *gadget = gsi->gadget; struct device *dev; struct device *gad_dev; + bool block_db; event = read_event(d_port); @@ -676,28 +675,6 @@ static void ipa_work_handler(struct work_struct *w) break; } - /* - * Update desc and reconfigure USB GSI OUT and IN - * endpoint for RNDIS Adaptor enable case. - */ - if (d_port->out_ep && !d_port->out_ep->desc && - gsi->out_ep_desc_backup) { - d_port->out_ep->desc = gsi->out_ep_desc_backup; - d_port->out_ep->ep_intr_num = 1; - log_event_dbg("%s: OUT ep_op_config", __func__); - usb_gsi_ep_op(d_port->out_ep, - &d_port->out_request, GSI_EP_OP_CONFIG); - } - - if (d_port->in_ep && !d_port->in_ep->desc && - gsi->in_ep_desc_backup) { - d_port->in_ep->desc = gsi->in_ep_desc_backup; - d_port->in_ep->ep_intr_num = 2; - log_event_dbg("%s: IN ep_op_config", __func__); - usb_gsi_ep_op(d_port->in_ep, - &d_port->in_request, GSI_EP_OP_CONFIG); - } - ipa_connect_channels(d_port); ipa_data_path_enable(d_port); d_port->sm_state = STATE_CONNECTED; @@ -759,7 +736,15 @@ static void ipa_work_handler(struct work_struct *w) if (event == EVT_HOST_NRDY) { log_event_dbg("%s: ST_CON_HOST_NRDY\n", __func__); - ipa_disconnect_handler(d_port); + block_db = true; + /* stop USB ringing doorbell to GSI(OUT_EP) */ + usb_gsi_ep_op(d_port->in_ep, (void *)&block_db, + GSI_EP_OP_SET_CLR_BLOCK_DBL); + gsi_rndis_ipa_reset_trigger(d_port); + usb_gsi_ep_op(d_port->in_ep, NULL, + GSI_EP_OP_ENDXFER); + usb_gsi_ep_op(d_port->out_ep, NULL, + GSI_EP_OP_ENDXFER); } ipa_disconnect_work_handler(d_port); @@ -1467,6 +1452,27 @@ static void gsi_rndis_open(struct f_gsi *rndis) rndis_signal_connect(rndis->params); } +static void gsi_rndis_ipa_reset_trigger(struct gsi_data_port *d_port) +{ + struct f_gsi *rndis = d_port_to_gsi(d_port); + unsigned long flags; + + if (!rndis) { + log_event_err("%s: gsi prot ctx is %pK", __func__, rndis); + return; + } + + spin_lock_irqsave(&rndis->d_port.lock, flags); + if (!rndis) { + log_event_err("%s: No RNDIS instance", __func__); + spin_unlock_irqrestore(&rndis->d_port.lock, flags); + return; + } + + rndis->d_port.net_ready_trigger = false; + spin_unlock_irqrestore(&rndis->d_port.lock, flags); +} + void gsi_rndis_flow_ctrl_enable(bool enable, struct rndis_params *param) { struct f_gsi *rndis = param->v; diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 99285b416308..721b4c18f8db 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -223,6 +223,13 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer, /* pick the first one */ list = list_first_entry(&hidg->completed_out_req, struct f_hidg_req_list, list); + + /* + * Remove this from list to protect it from beign free() + * while host disables our function + */ + list_del(&list->list); + req = list->req; count = min_t(unsigned int, count, req->actual - list->pos); spin_unlock_irqrestore(&hidg->spinlock, flags); @@ -238,15 +245,20 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer, * call, taking into account its current read position. */ if (list->pos == req->actual) { - spin_lock_irqsave(&hidg->spinlock, flags); - list_del(&list->list); kfree(list); - spin_unlock_irqrestore(&hidg->spinlock, flags); req->length = hidg->report_length; ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL); - if (ret < 0) + if (ret < 0) { + free_ep_req(hidg->out_ep, req); return ret; + } + } else { + spin_lock_irqsave(&hidg->spinlock, flags); + list_add(&list->list, &hidg->completed_out_req); + spin_unlock_irqrestore(&hidg->spinlock, flags); + + wake_up(&hidg->read_queue); } return count; @@ -490,14 +502,18 @@ static void hidg_disable(struct usb_function *f) { struct f_hidg *hidg = func_to_hidg(f); struct f_hidg_req_list *list, *next; + unsigned long flags; usb_ep_disable(hidg->in_ep); usb_ep_disable(hidg->out_ep); + spin_lock_irqsave(&hidg->spinlock, flags); list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) { + free_ep_req(hidg->out_ep, list->req); list_del(&list->list); kfree(list); } + spin_unlock_irqrestore(&hidg->spinlock, flags); } static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 79f554f1fb23..1fcdbdc35cd1 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -210,12 +210,6 @@ static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep, return alloc_ep_req(ep, length, length); } -static void free_ep_req(struct usb_ep *ep, struct usb_request *req) -{ - kfree(req->buf); - usb_ep_free_request(ep, req); -} - static const uint8_t f_midi_cin_length[] = { 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1 }; diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index 9f3ced62d916..67b243989938 100644 --- a/drivers/usb/gadget/function/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c @@ -303,12 +303,6 @@ static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len) return alloc_ep_req(ep, len, ss->buflen); } -void free_ep_req(struct usb_ep *ep, struct usb_request *req) -{ - kfree(req->buf); - usb_ep_free_request(ep, req); -} - static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) { int value; diff --git a/drivers/usb/gadget/function/g_zero.h b/drivers/usb/gadget/function/g_zero.h index 15f180904f8a..5ed90b437f18 100644 --- a/drivers/usb/gadget/function/g_zero.h +++ b/drivers/usb/gadget/function/g_zero.h @@ -59,7 +59,6 @@ void lb_modexit(void); int lb_modinit(void); /* common utilities */ -void free_ep_req(struct usb_ep *ep, struct usb_request *req); void disable_endpoints(struct usb_composite_dev *cdev, struct usb_ep *in, struct usb_ep *out, struct usb_ep *iso_in, struct usb_ep *iso_out); diff --git a/drivers/usb/gadget/u_f.c b/drivers/usb/gadget/u_f.c index c6276f0268ae..4bc7eea8bfc8 100644 --- a/drivers/usb/gadget/u_f.c +++ b/drivers/usb/gadget/u_f.c @@ -11,7 +11,6 @@ * published by the Free Software Foundation. */ -#include #include "u_f.h" struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len) diff --git a/drivers/usb/gadget/u_f.h b/drivers/usb/gadget/u_f.h index 1d5f0eb68552..4247cc098a89 100644 --- a/drivers/usb/gadget/u_f.h +++ b/drivers/usb/gadget/u_f.h @@ -16,6 +16,8 @@ #ifndef __U_F_H__ #define __U_F_H__ +#include + /* Variable Length Array Macros **********************************************/ #define vla_group(groupname) size_t groupname##__next = 0 #define vla_group_size(groupname) groupname##__next @@ -45,8 +47,12 @@ struct usb_ep; struct usb_request; +/* Requests allocated via alloc_ep_req() must be freed by free_ep_req(). */ struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len); +static inline void free_ep_req(struct usb_ep *ep, struct usb_request *req) +{ + kfree(req->buf); + usb_ep_free_request(ep, req); +} #endif /* __U_F_H__ */ - - diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c index 2bc3c6fa417a..3ffb20c4a207 100644 --- a/drivers/usb/phy/phy-msm-ssusb-qmp.c +++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c @@ -1,5 +1,5 @@ /* - * 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 @@ -473,11 +473,11 @@ static int msm_ssphy_qmp_set_suspend(struct usb_phy *uphy, int suspend) } if (suspend) { - if (!phy->cable_connected) + if (phy->cable_connected) + msm_ssusb_qmp_enable_autonomous(phy, 1); + else writel_relaxed(0x00, phy->base + phy->phy_reg[USB3_PHY_POWER_DOWN_CONTROL]); - else - msm_ssusb_qmp_enable_autonomous(phy, 1); /* Make sure above write completed with PHY */ wmb(); @@ -540,6 +540,10 @@ static int msm_ssphy_qmp_notify_disconnect(struct usb_phy *uphy, struct msm_ssphy_qmp *phy = container_of(uphy, struct msm_ssphy_qmp, phy); + writel_relaxed(0x00, + phy->base + phy->phy_reg[USB3_PHY_POWER_DOWN_CONTROL]); + readl_relaxed(phy->base + phy->phy_reg[USB3_PHY_POWER_DOWN_CONTROL]); + dev_dbg(uphy->dev, "QMP phy disconnect notification\n"); dev_dbg(uphy->dev, " cable_connected=%d\n", phy->cable_connected); phy->cable_connected = false; diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index 3806e7014199..2938153fe7b1 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -189,6 +189,7 @@ static int usb_console_setup(struct console *co, char *options) tty_kref_put(tty); reset_open_count: port->port.count = 0; + info->port = NULL; usb_autopm_put_interface(serial->interface); error_get_interface: usb_serial_put(serial); diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h index f58caa9e6a27..a155cd02bce2 100644 --- a/drivers/usb/storage/uas-detect.h +++ b/drivers/usb/storage/uas-detect.h @@ -9,7 +9,8 @@ static int uas_is_interface(struct usb_host_interface *intf) intf->desc.bInterfaceProtocol == USB_PR_UAS); } -static int uas_find_uas_alt_setting(struct usb_interface *intf) +static struct usb_host_interface *uas_find_uas_alt_setting( + struct usb_interface *intf) { int i; @@ -17,10 +18,10 @@ static int uas_find_uas_alt_setting(struct usb_interface *intf) struct usb_host_interface *alt = &intf->altsetting[i]; if (uas_is_interface(alt)) - return alt->desc.bAlternateSetting; + return alt; } - return -ENODEV; + return NULL; } static int uas_find_endpoints(struct usb_host_interface *alt, @@ -58,14 +59,14 @@ static int uas_use_uas_driver(struct usb_interface *intf, struct usb_device *udev = interface_to_usbdev(intf); struct usb_hcd *hcd = bus_to_hcd(udev->bus); unsigned long flags = id->driver_info; - int r, alt; - + struct usb_host_interface *alt; + int r; alt = uas_find_uas_alt_setting(intf); - if (alt < 0) + if (!alt) return 0; - r = uas_find_endpoints(&intf->altsetting[alt], eps); + r = uas_find_endpoints(alt, eps); if (r < 0) return 0; diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index e26e32169a36..f952635ebe5f 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -849,14 +849,14 @@ MODULE_DEVICE_TABLE(usb, uas_usb_ids); static int uas_switch_interface(struct usb_device *udev, struct usb_interface *intf) { - int alt; + struct usb_host_interface *alt; alt = uas_find_uas_alt_setting(intf); - if (alt < 0) - return alt; + if (!alt) + return -ENODEV; - return usb_set_interface(udev, - intf->altsetting[0].desc.bInterfaceNumber, alt); + return usb_set_interface(udev, alt->desc.bInterfaceNumber, + alt->desc.bAlternateSetting); } static int uas_configure_endpoints(struct uas_dev_info *devinfo) diff --git a/drivers/video/fbdev/msm/mdp3_ctrl.c b/drivers/video/fbdev/msm/mdp3_ctrl.c index d357a616b05e..8a9e8acf6c0e 100644 --- a/drivers/video/fbdev/msm/mdp3_ctrl.c +++ b/drivers/video/fbdev/msm/mdp3_ctrl.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 @@ -1563,12 +1563,16 @@ static int mdp3_get_metadata(struct msm_fb_data_type *mfd, } break; case metadata_op_get_ion_fd: - if (mfd->fb_ion_handle) { + if (mfd->fb_ion_handle && mfd->fb_ion_client) { + get_dma_buf(mfd->fbmem_buf); metadata->data.fbmem_ionfd = - dma_buf_fd(mfd->fbmem_buf, 0); - if (metadata->data.fbmem_ionfd < 0) + ion_share_dma_buf_fd(mfd->fb_ion_client, + mfd->fb_ion_handle); + if (metadata->data.fbmem_ionfd < 0) { + dma_buf_put(mfd->fbmem_buf); pr_err("fd allocation failed. fd = %d\n", - metadata->data.fbmem_ionfd); + metadata->data.fbmem_ionfd); + } } break; default: diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index 5548f0f09f8a..1ccb27113c11 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.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 @@ -255,6 +255,13 @@ struct mdss_scaler_block { u32 *dest_scaler_off; u32 *dest_scaler_lut_off; struct mdss_mdp_qseed3_lut_tbl lut_tbl; + + /* + * Lock is mainly to serialize access to LUT. + * LUT values come asynchronously from userspace + * via ioctl. + */ + struct mutex scaler_lock; }; struct mdss_data_type; diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c index 2f5aad8ed801..8c28fbf8fbc2 100644 --- a/drivers/video/fbdev/msm/mdss_compat_utils.c +++ b/drivers/video/fbdev/msm/mdss_compat_utils.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. * Copyright (C) 1994 Martin Schaller * * 2001 - Documented with DocBook @@ -2853,26 +2853,28 @@ static int __pp_compat_alloc(struct msmfb_mdp_pp32 __user *pp32, *pp = compat_alloc_user_space(alloc_size); if (NULL == *pp) return -ENOMEM; - memset(*pp, 0, alloc_size); - - (*pp)->data.lut_cfg_data.data.pgc_lut_data.r_data = - (struct mdp_ar_gc_lut_data *) - ((unsigned long) *pp + - sizeof(struct msmfb_mdp_pp)); - (*pp)->data.lut_cfg_data.data.pgc_lut_data.g_data = - (struct mdp_ar_gc_lut_data *) + if (clear_user(*pp, alloc_size)) + return -EFAULT; + if (put_user((struct mdp_ar_gc_lut_data *) + ((unsigned long) *pp + + sizeof(struct msmfb_mdp_pp)), + &(*pp)->data.lut_cfg_data.data.pgc_lut_data.r_data) || + put_user((struct mdp_ar_gc_lut_data *) ((unsigned long) *pp + sizeof(struct msmfb_mdp_pp) + - pgc_size); - (*pp)->data.lut_cfg_data.data.pgc_lut_data.b_data = - (struct mdp_ar_gc_lut_data *) + pgc_size), + &(*pp)->data.lut_cfg_data.data.pgc_lut_data.g_data) || + put_user((struct mdp_ar_gc_lut_data *) ((unsigned long) *pp + sizeof(struct msmfb_mdp_pp) + - (2 * pgc_size)); - (*pp)->data.lut_cfg_data.data.pgc_lut_data.cfg_payload - = (void *)((unsigned long) *pp + + (2 * pgc_size)), + &(*pp)->data.lut_cfg_data.data.pgc_lut_data.b_data) || + put_user((void *)((unsigned long) *pp + sizeof(struct msmfb_mdp_pp) + - (3 * pgc_size)); + (3 * pgc_size)), + &(*pp)->data.lut_cfg_data.data. + pgc_lut_data.cfg_payload)) + return -EFAULT; break; case mdp_lut_igc: alloc_size += __pp_compat_size_igc(); @@ -2882,10 +2884,13 @@ static int __pp_compat_alloc(struct msmfb_mdp_pp32 __user *pp32, alloc_size); return -ENOMEM; } - memset(*pp, 0, alloc_size); - (*pp)->data.lut_cfg_data.data.igc_lut_data.cfg_payload - = (void *)((unsigned long)(*pp) + - sizeof(struct msmfb_mdp_pp)); + if (clear_user(*pp, alloc_size)) + return -EFAULT; + if (put_user((void *)((unsigned long)(*pp) + + sizeof(struct msmfb_mdp_pp)), + &(*pp)->data.lut_cfg_data.data. + igc_lut_data.cfg_payload)) + return -EFAULT; break; case mdp_lut_hist: alloc_size += __pp_compat_size_hist_lut(); @@ -2895,10 +2900,13 @@ static int __pp_compat_alloc(struct msmfb_mdp_pp32 __user *pp32, alloc_size); return -ENOMEM; } - memset(*pp, 0, alloc_size); - (*pp)->data.lut_cfg_data.data.hist_lut_data.cfg_payload - = (void *)((unsigned long)(*pp) + - sizeof(struct msmfb_mdp_pp)); + if (clear_user(*pp, alloc_size)) + return -EFAULT; + if (put_user((void *)((unsigned long)(*pp) + + sizeof(struct msmfb_mdp_pp)), + &(*pp)->data.lut_cfg_data.data. + hist_lut_data.cfg_payload)) + return -EFAULT; break; default: *pp = compat_alloc_user_space(alloc_size); @@ -2907,7 +2915,8 @@ static int __pp_compat_alloc(struct msmfb_mdp_pp32 __user *pp32, alloc_size, lut_type); return -ENOMEM; } - memset(*pp, 0, alloc_size); + if (clear_user(*pp, alloc_size)) + return -EFAULT; break; } break; @@ -2919,10 +2928,12 @@ static int __pp_compat_alloc(struct msmfb_mdp_pp32 __user *pp32, alloc_size); return -ENOMEM; } - memset(*pp, 0, alloc_size); - (*pp)->data.pcc_cfg_data.cfg_payload = - (void *)((unsigned long)(*pp) + - sizeof(struct msmfb_mdp_pp)); + if (clear_user(*pp, alloc_size)) + return -EFAULT; + if (put_user((void *)((unsigned long)(*pp) + + sizeof(struct msmfb_mdp_pp)), + &(*pp)->data.pcc_cfg_data.cfg_payload)) + return -EFAULT; break; case mdp_op_gamut_cfg: alloc_size += __pp_compat_size_gamut(); @@ -2932,10 +2943,12 @@ static int __pp_compat_alloc(struct msmfb_mdp_pp32 __user *pp32, alloc_size); return -ENOMEM; } - memset(*pp, 0, alloc_size); - (*pp)->data.gamut_cfg_data.cfg_payload = - (void *)((unsigned long)(*pp) + - sizeof(struct msmfb_mdp_pp)); + if (clear_user(*pp, alloc_size)) + return -EFAULT; + if (put_user((void *)((unsigned long)(*pp) + + sizeof(struct msmfb_mdp_pp)), + &(*pp)->data.gamut_cfg_data.cfg_payload)) + return -EFAULT; break; case mdp_op_pa_v2_cfg: alloc_size += __pp_compat_size_pa(); @@ -2945,16 +2958,19 @@ static int __pp_compat_alloc(struct msmfb_mdp_pp32 __user *pp32, alloc_size); return -ENOMEM; } - memset(*pp, 0, alloc_size); - (*pp)->data.pa_v2_cfg_data.cfg_payload = - (void *)((unsigned long)(*pp) + - sizeof(struct msmfb_mdp_pp)); + if (clear_user(*pp, alloc_size)) + return -EFAULT; + if (put_user((void *)((unsigned long)(*pp) + + sizeof(struct msmfb_mdp_pp)), + &(*pp)->data.pa_v2_cfg_data.cfg_payload)) + return -EFAULT; break; default: *pp = compat_alloc_user_space(alloc_size); if (NULL == *pp) return -ENOMEM; - memset(*pp, 0, alloc_size); + if (clear_user(*pp, alloc_size)) + return -EFAULT; break; } return 0; @@ -3372,7 +3388,9 @@ static int mdss_histo_compat_ioctl(struct fb_info *info, unsigned int cmd, sizeof(struct mdp_histogram_start_req)); return -EINVAL; } - memset(hist_req, 0, sizeof(struct mdp_histogram_start_req)); + if (clear_user(hist_req, + sizeof(struct mdp_histogram_start_req))) + return -EFAULT; ret = __from_user_hist_start_req(hist_req32, hist_req); if (ret) goto histo_compat_err; @@ -3392,7 +3410,8 @@ static int mdss_histo_compat_ioctl(struct fb_info *info, unsigned int cmd, sizeof(struct mdp_histogram_data)); return -EINVAL; } - memset(hist, 0, sizeof(struct mdp_histogram_data)); + if (clear_user(hist, sizeof(struct mdp_histogram_data))) + return -EFAULT; ret = __from_user_hist_data(hist32, hist); if (ret) goto histo_compat_err; @@ -3895,7 +3914,7 @@ static int __to_user_mdp_overlay(struct mdp_overlay32 __user *ov32, } -static int __from_user_mdp_overlay(struct mdp_overlay *ov, +static int __from_user_mdp_overlay(struct mdp_overlay __user *ov, struct mdp_overlay32 __user *ov32) { __u32 data; @@ -3954,12 +3973,12 @@ static int __from_user_mdp_overlay(struct mdp_overlay *ov, return 0; } -static int __from_user_mdp_overlaylist(struct mdp_overlay_list *ovlist, - struct mdp_overlay_list32 *ovlist32, +static int __from_user_mdp_overlaylist(struct mdp_overlay_list __user *ovlist, + struct mdp_overlay_list32 __user *ovlist32, struct mdp_overlay **to_list_head) { __u32 i, ret; - unsigned long data, from_list_head; + unsigned long data, from_list_head, num_overlays; struct mdp_overlay32 *iter; if (!to_list_head || !ovlist32 || !ovlist) { @@ -3980,11 +3999,13 @@ static int __from_user_mdp_overlaylist(struct mdp_overlay_list *ovlist, sizeof(ovlist32->processed_overlays))) return -EFAULT; - if (get_user(data, &ovlist32->overlay_list)) { + if (get_user(data, &ovlist32->overlay_list) || + get_user(num_overlays, &ovlist32->num_overlays)) { ret = -EFAULT; goto validate_exit; } - for (i = 0; i < ovlist32->num_overlays; i++) { + + for (i = 0; i < num_overlays; i++) { if (get_user(from_list_head, (__u32 *)data + i)) { ret = -EFAULT; goto validate_exit; @@ -3997,7 +4018,8 @@ static int __from_user_mdp_overlaylist(struct mdp_overlay_list *ovlist, goto validate_exit; } } - ovlist->overlay_list = to_list_head; + if (put_user(to_list_head, &ovlist->overlay_list)) + return -EFAULT; return 0; @@ -4006,8 +4028,8 @@ static int __from_user_mdp_overlaylist(struct mdp_overlay_list *ovlist, return -EFAULT; } -static int __to_user_mdp_overlaylist(struct mdp_overlay_list32 *ovlist32, - struct mdp_overlay_list *ovlist, +static int __to_user_mdp_overlaylist(struct mdp_overlay_list32 __user *ovlist32, + struct mdp_overlay_list __user *ovlist, struct mdp_overlay **l_ptr) { __u32 i, ret; @@ -4080,31 +4102,33 @@ static u32 __pp_sspp_size(void) return size; } -static int __pp_sspp_set_offsets(struct mdp_overlay *ov) +static int __pp_sspp_set_offsets(struct mdp_overlay __user *ov) { if (!ov) { pr_err("invalid overlay pointer\n"); return -EFAULT; } - ov->overlay_pp_cfg.igc_cfg.cfg_payload = (void *)((unsigned long)ov + - sizeof(struct mdp_overlay)); - ov->overlay_pp_cfg.pa_v2_cfg_data.cfg_payload = - ov->overlay_pp_cfg.igc_cfg.cfg_payload + - sizeof(struct mdp_igc_lut_data_v1_7); - ov->overlay_pp_cfg.pcc_cfg_data.cfg_payload = - ov->overlay_pp_cfg.pa_v2_cfg_data.cfg_payload + - sizeof(struct mdp_pa_data_v1_7); - ov->overlay_pp_cfg.hist_lut_cfg.cfg_payload = - ov->overlay_pp_cfg.pcc_cfg_data.cfg_payload + - sizeof(struct mdp_pcc_data_v1_7); + if (put_user((void *)((unsigned long)ov + sizeof(struct mdp_overlay)), + &(ov->overlay_pp_cfg.igc_cfg.cfg_payload)) || + put_user(ov->overlay_pp_cfg.igc_cfg.cfg_payload + + sizeof(struct mdp_igc_lut_data_v1_7), + &(ov->overlay_pp_cfg.pa_v2_cfg_data.cfg_payload)) || + put_user(ov->overlay_pp_cfg.pa_v2_cfg_data.cfg_payload + + sizeof(struct mdp_pa_data_v1_7), + &(ov->overlay_pp_cfg.pcc_cfg_data.cfg_payload)) || + put_user(ov->overlay_pp_cfg.pcc_cfg_data.cfg_payload + + sizeof(struct mdp_pcc_data_v1_7), + &(ov->overlay_pp_cfg.hist_lut_cfg.cfg_payload))) + return -EFAULT; return 0; } int mdss_compat_overlay_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg, struct file *file) { - struct mdp_overlay *ov, **layers_head; - struct mdp_overlay32 *ov32; + struct mdp_overlay **layers_head; + struct mdp_overlay __user *ov; + struct mdp_overlay32 __user *ov32; struct mdp_overlay_list __user *ovlist; struct mdp_overlay_list32 __user *ovlist32; size_t layers_refs_sz, layers_sz, prepare_sz; diff --git a/drivers/video/fbdev/msm/mdss_debug_xlog.c b/drivers/video/fbdev/msm/mdss_debug_xlog.c index aeefc81657b0..10d747962a91 100644 --- a/drivers/video/fbdev/msm/mdss_debug_xlog.c +++ b/drivers/video/fbdev/msm/mdss_debug_xlog.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 @@ -755,6 +755,11 @@ static ssize_t mdss_xlog_dump_read(struct file *file, char __user *buff, if (__mdss_xlog_dump_calc_range()) { len = mdss_xlog_dump_entry(xlog_buf, MDSS_XLOG_BUF_MAX); + if (len < 0 || len > count) { + pr_err("len is more than the size of user buffer\n"); + return 0; + } + if (copy_to_user(buff, xlog_buf, len)) return -EFAULT; *ppos += len; diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index c82aad72c01c..d836cf333565 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.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. * Copyright (C) 2018 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify @@ -800,7 +800,7 @@ static ssize_t mdss_dsi_cmd_state_read(struct file *file, char __user *buf, if (blen < 0) return 0; - if (copy_to_user(buf, buffer, blen)) + if (copy_to_user(buf, buffer, min(count, (size_t)blen+1))) return -EFAULT; *ppos += blen; diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index 410d36a3ac31..0efb79d0b0e4 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -1,7 +1,7 @@ /* * MDSS MDP Interface (used by framebuffer core) * - * Copyright (c) 2007-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2007-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2007 Google Incorporated * * This software is licensed under the terms of the GNU General Public @@ -2033,8 +2033,8 @@ static void mdss_mdp_hw_rev_caps_init(struct mdss_data_type *mdata) mdata->hflip_buffer_reused = true; /* prevent disable of prefill calculations */ mdata->min_prefill_lines = 0xffff; - /* clock gating feature is enabled by default */ - mdata->enable_gate = true; + /* clock gating feature is disabled by default */ + mdata->enable_gate = false; mdata->pixel_ram_size = 0; mem_protect_sd_ctrl_id = MEM_PROTECT_SD_CTRL_FLAT; @@ -2436,6 +2436,8 @@ static u32 mdss_mdp_scaler_init(struct mdss_data_type *mdata, ret = mdss_mdp_ds_addr_setup(mdata); } + mutex_init(&mdata->scaler_off->scaler_lock); + return ret; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index 54fb21a5f35d..632d73e909a3 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.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 @@ -5912,9 +5912,7 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, } else { sctl_flush_bits = sctl->flush_bits; } - sctl->commit_in_progress = true; } - ctl->commit_in_progress = true; ctl_flush_bits = ctl->flush_bits; ATRACE_END("postproc_programming"); @@ -5928,6 +5926,9 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, MDP_COMMIT_STAGE_SETUP_DONE, commit_cb->data); ret = mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_READY); + ctl->commit_in_progress = true; + if (sctl) + sctl->commit_in_progress = true; /* * When wait for fence timed out, driver ignores the fences diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index c85dbb5ea82c..596fc77c09d5 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.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 @@ -3300,9 +3300,8 @@ int mdss_mdp_overlay_vsync_ctrl(struct msm_fb_data_type *mfd, int en) goto end; } - if (!ctl->panel_data->panel_info.cont_splash_enabled - && (!mdss_mdp_ctl_is_power_on(ctl) || - mdss_panel_is_power_on_ulp(ctl->power_state))) { + if (!ctl->panel_data->panel_info.cont_splash_enabled && + !mdss_mdp_ctl_is_power_on(ctl)) { pr_debug("fb%d vsync pending first update en=%d, ctl power state:%d\n", mfd->index, en, ctl->power_state); rc = -EPERM; @@ -6642,14 +6641,18 @@ static int mdss_mdp_scaler_lut_init(struct mdss_data_type *mdata, if (!mdata->scaler_off) return -EFAULT; + mutex_lock(&mdata->scaler_off->scaler_lock); + qseed3_lut_tbl = &mdata->scaler_off->lut_tbl; if ((lut_tbl->dir_lut_size != DIR_LUT_IDX * DIR_LUT_COEFFS * sizeof(uint32_t)) || (lut_tbl->cir_lut_size != CIR_LUT_IDX * CIR_LUT_COEFFS * sizeof(uint32_t)) || (lut_tbl->sep_lut_size != - SEP_LUT_IDX * SEP_LUT_COEFFS * sizeof(uint32_t))) - return -EINVAL; + SEP_LUT_IDX * SEP_LUT_COEFFS * sizeof(uint32_t))) { + mutex_unlock(&mdata->scaler_off->scaler_lock); + return -EINVAL; + } if (!qseed3_lut_tbl->dir_lut) { qseed3_lut_tbl->dir_lut = devm_kzalloc(&mdata->pdev->dev, @@ -6657,7 +6660,7 @@ static int mdss_mdp_scaler_lut_init(struct mdss_data_type *mdata, GFP_KERNEL); if (!qseed3_lut_tbl->dir_lut) { ret = -ENOMEM; - goto fail; + goto err; } } @@ -6667,7 +6670,7 @@ static int mdss_mdp_scaler_lut_init(struct mdss_data_type *mdata, GFP_KERNEL); if (!qseed3_lut_tbl->cir_lut) { ret = -ENOMEM; - goto fail; + goto fail_free_dir_lut; } } @@ -6677,44 +6680,52 @@ static int mdss_mdp_scaler_lut_init(struct mdss_data_type *mdata, GFP_KERNEL); if (!qseed3_lut_tbl->sep_lut) { ret = -ENOMEM; - goto fail; + goto fail_free_cir_lut; } } /* Invalidate before updating */ qseed3_lut_tbl->valid = false; - if (copy_from_user(qseed3_lut_tbl->dir_lut, (void *)(unsigned long)lut_tbl->dir_lut, lut_tbl->dir_lut_size)) { ret = -EINVAL; - goto err; + goto fail_free_sep_lut; } if (copy_from_user(qseed3_lut_tbl->cir_lut, (void *)(unsigned long)lut_tbl->cir_lut, lut_tbl->cir_lut_size)) { ret = -EINVAL; - goto err; + goto fail_free_sep_lut; } if (copy_from_user(qseed3_lut_tbl->sep_lut, (void *)(unsigned long)lut_tbl->sep_lut, lut_tbl->sep_lut_size)) { ret = -EINVAL; - goto err; + goto fail_free_sep_lut; } qseed3_lut_tbl->valid = true; + mutex_unlock(&mdata->scaler_off->scaler_lock); + return ret; -fail: - kfree(qseed3_lut_tbl->dir_lut); - kfree(qseed3_lut_tbl->cir_lut); - kfree(qseed3_lut_tbl->sep_lut); +fail_free_sep_lut: + devm_kfree(&mdata->pdev->dev, qseed3_lut_tbl->sep_lut); +fail_free_cir_lut: + devm_kfree(&mdata->pdev->dev, qseed3_lut_tbl->cir_lut); +fail_free_dir_lut: + devm_kfree(&mdata->pdev->dev, qseed3_lut_tbl->dir_lut); err: + qseed3_lut_tbl->dir_lut = NULL; + qseed3_lut_tbl->cir_lut = NULL; + qseed3_lut_tbl->sep_lut = NULL; qseed3_lut_tbl->valid = false; + mutex_unlock(&mdata->scaler_off->scaler_lock); + return ret; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c index 9c2b1d42bd35..5b9798e2c24e 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.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 @@ -453,6 +453,13 @@ static u32 igc_limited[IGC_LUT_ENTRIES] = { #define PP_FLAGS_DIRTY_SHARP 0x200 #define PP_FLAGS_DIRTY_PA_DITHER 0x400 +#define PP_EARLY_PROGRAM_DIRTY_MASK (PP_FLAGS_DIRTY_PCC | \ + PP_FLAGS_DIRTY_ENHIST | PP_FLAGS_DIRTY_HIST_COL) +#define PP_DEFERRED_PROGRAM_DIRTY_MASK (PP_FLAGS_DIRTY_IGC | \ + PP_FLAGS_DIRTY_PGC | PP_FLAGS_DIRTY_ARGC | \ + PP_FLAGS_DIRTY_GAMUT | PP_FLAGS_DIRTY_PA | \ + PP_FLAGS_DIRTY_DITHER | PP_FLAGS_DIRTY_PA_DITHER) + /* Leave space for future features */ #define PP_FLAGS_RESUME_COMMIT 0x10000000 @@ -1604,11 +1611,16 @@ int mdss_mdp_scaler_lut_cfg(struct mdp_scale_data_v2 *scaler, }; mdata = mdss_mdp_get_mdata(); + + mutex_lock(&mdata->scaler_off->scaler_lock); + lut_tbl = &mdata->scaler_off->lut_tbl; if ((!lut_tbl) || (!lut_tbl->valid)) { + mutex_unlock(&mdata->scaler_off->scaler_lock); pr_err("%s:Invalid QSEED3 LUT TABLE\n", __func__); return -EINVAL; } + if ((scaler->lut_flag & SCALER_LUT_DIR_WR) || (scaler->lut_flag & SCALER_LUT_Y_CIR_WR) || (scaler->lut_flag & SCALER_LUT_UV_CIR_WR) || @@ -1656,6 +1668,8 @@ int mdss_mdp_scaler_lut_cfg(struct mdp_scale_data_v2 *scaler, } } + mutex_unlock(&mdata->scaler_off->scaler_lock); + return 0; } @@ -2853,10 +2867,15 @@ int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl, } } + if (info->pp_program_mask & PP_NORMAL_PROGRAM_MASK) { + mdss_pp_res->pp_disp_flags[disp_num] &= + ~PP_EARLY_PROGRAM_DIRTY_MASK; + } if (info->pp_program_mask & PP_DEFER_PROGRAM_MASK) { /* clear dirty flag */ if (disp_num < MDSS_BLOCK_DISP_NUM) { - mdss_pp_res->pp_disp_flags[disp_num] = 0; + mdss_pp_res->pp_disp_flags[disp_num] &= + ~PP_DEFERRED_PROGRAM_DIRTY_MASK; if (disp_num < mdata->nad_cfgs) mdata->ad_cfgs[disp_num].reg_sts = 0; } diff --git a/fs/block_dev.c b/fs/block_dev.c index d3c296d4eb25..43b80ca84d9c 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -558,6 +558,8 @@ static void bdev_evict_inode(struct inode *inode) } list_del_init(&bdev->bd_list); spin_unlock(&bdev_lock); + /* Detach inode from wb early as bdi_put() may free bdi->wb */ + inode_detach_wb(inode); if (bdev->bd_bdi != &noop_backing_dev_info) { bdi_put(bdev->bd_bdi); bdev->bd_bdi = &noop_backing_dev_info; @@ -1221,8 +1223,6 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) bdev->bd_disk = disk; bdev->bd_queue = disk->queue; bdev->bd_contains = bdev; - if (bdev->bd_bdi == &noop_backing_dev_info) - bdev->bd_bdi = bdi_get(disk->queue->backing_dev_info); bdev->bd_inode->i_flags = disk->fops->direct_access ? S_DAX : 0; if (!partno) { @@ -1294,6 +1294,9 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) (bdev->bd_part->nr_sects % (PAGE_SIZE / 512))) bdev->bd_inode->i_flags &= ~S_DAX; } + + if (bdev->bd_bdi == &noop_backing_dev_info) + bdev->bd_bdi = bdi_get(disk->queue->backing_dev_info); } else { if (bdev->bd_contains == bdev) { ret = 0; @@ -1325,8 +1328,6 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) bdev->bd_disk = NULL; bdev->bd_part = NULL; bdev->bd_queue = NULL; - bdi_put(bdev->bd_bdi); - bdev->bd_bdi = &noop_backing_dev_info; if (bdev != bdev->bd_contains) __blkdev_put(bdev->bd_contains, mode, 1); bdev->bd_contains = NULL; @@ -1548,12 +1549,6 @@ static void __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part) kill_bdev(bdev); bdev_write_inode(bdev); - /* - * Detaching bdev inode from its wb in __destroy_inode() - * is too late: the queue which embeds its bdi (along with - * root wb) can be gone as soon as we put_disk() below. - */ - inode_detach_wb(bdev->bd_inode); } if (bdev->bd_contains == bdev) { if (disk->fops->release) diff --git a/fs/ext4/crypto.c b/fs/ext4/crypto.c index b9f838af5a72..f5099a3386ec 100644 --- a/fs/ext4/crypto.c +++ b/fs/ext4/crypto.c @@ -389,14 +389,12 @@ int ext4_decrypt(struct page *page) page->index, page, page, GFP_NOFS); } -int ext4_encrypted_zeroout(struct inode *inode, struct ext4_extent *ex) +int ext4_encrypted_zeroout(struct inode *inode, ext4_lblk_t lblk, + ext4_fsblk_t pblk, ext4_lblk_t len) { struct ext4_crypto_ctx *ctx; struct page *ciphertext_page = NULL; struct bio *bio; - ext4_lblk_t lblk = le32_to_cpu(ex->ee_block); - ext4_fsblk_t pblk = ext4_ext_pblock(ex); - unsigned int len = ext4_ext_get_actual_len(ex); int ret, err = 0; #if 0 diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c index 363e4c6bf37f..307390e58a4a 100644 --- a/fs/ext4/crypto_key.c +++ b/fs/ext4/crypto_key.c @@ -220,11 +220,9 @@ int _ext4_get_encryption_info(struct inode *inode) int mode; int res; - if (!ext4_read_workqueue) { - res = ext4_init_crypto(); - if (res) - return res; - } + res = ext4_init_crypto(); + if (res) + return res; retry: crypt_info = ACCESS_ONCE(ei->i_crypt_info); diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 6c910f127f1f..abc9e169cb44 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -2271,7 +2271,8 @@ struct page *ext4_encrypt(struct inode *inode, struct page *plaintext_page, gfp_t gfp_flags); int ext4_decrypt(struct page *page); -int ext4_encrypted_zeroout(struct inode *inode, struct ext4_extent *ex); +int ext4_encrypted_zeroout(struct inode *inode, ext4_lblk_t lblk, + ext4_fsblk_t pblk, ext4_lblk_t len); extern const struct dentry_operations ext4_encrypted_d_ops; #ifdef CONFIG_EXT4_FS_ENCRYPTION @@ -2539,6 +2540,8 @@ extern int ext4_filemap_fault(struct vm_area_struct *vma, struct vm_fault *vmf); extern qsize_t *ext4_get_reserved_space(struct inode *inode); extern void ext4_da_update_reserve_space(struct inode *inode, int used, int quota_claim); +extern int ext4_issue_zeroout(struct inode *inode, ext4_lblk_t lblk, + ext4_fsblk_t pblk, ext4_lblk_t len); /* indirect.c */ extern int ext4_ind_map_blocks(handle_t *handle, struct inode *inode, diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 61d5bfc7318c..fc496c646d12 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -3127,19 +3127,11 @@ static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex) { ext4_fsblk_t ee_pblock; unsigned int ee_len; - int ret; ee_len = ext4_ext_get_actual_len(ex); ee_pblock = ext4_ext_pblock(ex); - - if (ext4_encrypted_inode(inode)) - return ext4_encrypted_zeroout(inode, ex); - - ret = sb_issue_zeroout(inode->i_sb, ee_pblock, ee_len, GFP_NOFS); - if (ret > 0) - ret = 0; - - return ret; + return ext4_issue_zeroout(inode, le32_to_cpu(ex->ee_block), ee_pblock, + ee_len); } /* diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 39db0f2b17a0..21047f1cc177 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -390,6 +390,21 @@ static int __check_block_validity(struct inode *inode, const char *func, return 0; } +int ext4_issue_zeroout(struct inode *inode, ext4_lblk_t lblk, ext4_fsblk_t pblk, + ext4_lblk_t len) +{ + int ret; + + if (ext4_encrypted_inode(inode)) + return ext4_encrypted_zeroout(inode, lblk, pblk, len); + + ret = sb_issue_zeroout(inode->i_sb, pblk, len, GFP_NOFS); + if (ret > 0) + ret = 0; + + return ret; +} + #define check_block_validity(inode, map) \ __check_block_validity((inode), __func__, __LINE__, (map)) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index ca7d46de5ca3..028f38f0906c 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1947,8 +1947,10 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, err = copy_out_args(cs, &req->out, nbytes); if (req->in.h.opcode == FUSE_CANONICAL_PATH) { - req->out.h.error = kern_path((char *)req->out.args[0].value, 0, - req->canonical_path); + char *path = (char *)req->out.args[0].value; + + path[req->out.args[0].size - 1] = 0; + req->out.h.error = kern_path(path, 0, req->canonical_path); } fuse_copy_finish(cs); diff --git a/fs/nsfs.c b/fs/nsfs.c index 8f20d6016e20..dbb7e43cbb5b 100644 --- a/fs/nsfs.c +++ b/fs/nsfs.c @@ -95,7 +95,8 @@ void *ns_get_path(struct path *path, struct task_struct *task, return ERR_PTR(-ENOMEM); } d_instantiate(dentry, inode); - dentry->d_fsdata = (void *)ns_ops; + dentry->d_flags |= DCACHE_RCUACCESS; + dentry->d_fsdata = (void *)ns->ops; d = atomic_long_cmpxchg(&ns->stashed, 0, (unsigned long)dentry); if (d) { d_delete(dentry); /* make sure ->d_prune() does nothing */ diff --git a/fs/proc/meminfo.c b/fs/proc/meminfo.c index 9155a5a0d3b9..b7594b9fa5fa 100644 --- a/fs/proc/meminfo.c +++ b/fs/proc/meminfo.c @@ -79,6 +79,13 @@ static int meminfo_proc_show(struct seq_file *m, void *v) available += global_page_state(NR_SLAB_RECLAIMABLE) - min(global_page_state(NR_SLAB_RECLAIMABLE) / 2, wmark_low); + /* + * Part of the kernel memory, which can be released under memory + * pressure. + */ + available += global_page_state(NR_INDIRECTLY_RECLAIMABLE_BYTES) >> + PAGE_SHIFT; + if (available < 0) available = 0; diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 1d9ef8229cd6..75ffb7c889c8 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c @@ -814,8 +814,8 @@ static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct return err; } -static int sdcardfs_fillattr(struct vfsmount *mnt, - struct inode *inode, struct kstat *stat) +static int sdcardfs_fillattr(struct vfsmount *mnt, struct inode *inode, + struct kstat *lower_stat, struct kstat *stat) { struct sdcardfs_inode_info *info = SDCARDFS_I(inode); struct sdcardfs_inode_data *top = top_data_get(info); @@ -830,12 +830,12 @@ static int sdcardfs_fillattr(struct vfsmount *mnt, stat->uid = make_kuid(&init_user_ns, top->d_uid); stat->gid = make_kgid(&init_user_ns, get_gid(mnt, top)); stat->rdev = inode->i_rdev; - stat->size = i_size_read(inode); - stat->atime = inode->i_atime; - stat->mtime = inode->i_mtime; - stat->ctime = inode->i_ctime; - stat->blksize = (1 << inode->i_blkbits); - stat->blocks = inode->i_blocks; + stat->size = lower_stat->size; + stat->atime = lower_stat->atime; + stat->mtime = lower_stat->mtime; + stat->ctime = lower_stat->ctime; + stat->blksize = lower_stat->blksize; + stat->blocks = lower_stat->blocks; data_put(top); return 0; } @@ -861,8 +861,7 @@ static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, goto out; sdcardfs_copy_and_fix_attrs(d_inode(dentry), d_inode(lower_path.dentry)); - err = sdcardfs_fillattr(mnt, d_inode(dentry), stat); - stat->blocks = lower_stat.blocks; + err = sdcardfs_fillattr(mnt, d_inode(dentry), &lower_stat, stat); out: sdcardfs_put_lower_path(dentry, &lower_path); return err; diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h index 88b92b2f1872..d9fb3ce1874e 100644 --- a/fs/sdcardfs/sdcardfs.h +++ b/fs/sdcardfs/sdcardfs.h @@ -656,7 +656,7 @@ static inline bool str_n_case_eq(const char *s1, const char *s2, size_t len) static inline bool qstr_case_eq(const struct qstr *q1, const struct qstr *q2) { - return q1->len == q2->len && str_case_eq(q1->name, q2->name); + return q1->len == q2->len && str_n_case_eq(q1->name, q2->name, q2->len); } #define QSTR_LITERAL(string) QSTR_INIT(string, sizeof(string)-1) diff --git a/include/crypto/internal/hash.h b/include/crypto/internal/hash.h index 611b3d3bbab5..0ebdb4f2f0c8 100644 --- a/include/crypto/internal/hash.h +++ b/include/crypto/internal/hash.h @@ -83,6 +83,14 @@ int ahash_register_instance(struct crypto_template *tmpl, struct ahash_instance *inst); void ahash_free_instance(struct crypto_instance *inst); +int shash_no_setkey(struct crypto_shash *tfm, const u8 *key, + unsigned int keylen); + +static inline bool crypto_shash_alg_has_setkey(struct shash_alg *alg) +{ + return alg->setkey != shash_no_setkey; +} + int crypto_init_ahash_spawn(struct crypto_ahash_spawn *spawn, struct hash_alg_common *alg, struct crypto_instance *inst); diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index b54b1a748d83..70fd47e111b8 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -5,6 +5,8 @@ * Copyright (C) 2011 Samsung Electronics * MyungJoo Ham * + * 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 as * published by the Free Software Foundation. @@ -201,6 +203,14 @@ extern void devm_devfreq_remove_device(struct device *dev, extern int devfreq_suspend_device(struct devfreq *devfreq); extern int devfreq_resume_device(struct devfreq *devfreq); +/** + * update_devfreq() - Reevaluate the device and configure frequency + * @devfreq: the devfreq device + * + * Note: devfreq->lock must be held + */ +extern int update_devfreq(struct devfreq *devfreq); + /* Helper functions for devfreq user device driver with OPP. */ extern struct dev_pm_opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq, u32 flags); diff --git a/include/linux/devfreq_cooling.h b/include/linux/devfreq_cooling.h index 7adf6cc4b305..d465353234c8 100644 --- a/include/linux/devfreq_cooling.h +++ b/include/linux/devfreq_cooling.h @@ -4,6 +4,8 @@ * * Copyright (C) 2014-2015 ARM Limited * + * 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 as * published by the Free Software Foundation. @@ -20,7 +22,6 @@ #include #include -#ifdef CONFIG_DEVFREQ_THERMAL /** * struct devfreq_cooling_power - Devfreq cooling power ops @@ -43,6 +44,8 @@ struct devfreq_cooling_power { unsigned long dyn_power_coeff; }; +#ifdef CONFIG_DEVFREQ_THERMAL + struct thermal_cooling_device * of_devfreq_cooling_register_power(struct device_node *np, struct devfreq *df, struct devfreq_cooling_power *dfc_power); diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index bcece98d46cb..73523fad50b6 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -226,7 +226,7 @@ extern void mmc_cmdq_clk_scaling_start_busy(struct mmc_host *host, bool lock_needed); extern void mmc_cmdq_clk_scaling_stop_busy(struct mmc_host *host, bool lock_needed, bool is_cmdq_dcmd); -extern void mmc_recovery_fallback_lower_speed(struct mmc_host *host); +extern int mmc_recovery_fallback_lower_speed(struct mmc_host *host); /** * mmc_claim_host - exclusively claim a host diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 2b1be7efde55..862503e713c2 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -175,6 +175,7 @@ enum zone_stat_item { NR_ANON_TRANSPARENT_HUGEPAGES, NR_FREE_CMA_PAGES, NR_SWAPCACHE, + NR_INDIRECTLY_RECLAIMABLE_BYTES, /* measured in bytes */ NR_VM_ZONE_STAT_ITEMS }; /* diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 8b8a46ce32d0..64d0797cc3a7 100755 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -103,6 +103,9 @@ enum { POWER_SUPPLY_DP_DM_HVDCP3_SUPPORTED = 10, POWER_SUPPLY_DP_DM_ICL_DOWN = 11, POWER_SUPPLY_DP_DM_ICL_UP = 12, + POWER_SUPPLY_DP_DM_FORCE_5V = 13, + POWER_SUPPLY_DP_DM_FORCE_9V = 14, + POWER_SUPPLY_DP_DM_FORCE_12V = 15, }; enum { diff --git a/include/linux/psci.h b/include/linux/psci.h index 393efe2edf9a..66499dd612f5 100644 --- a/include/linux/psci.h +++ b/include/linux/psci.h @@ -28,6 +28,7 @@ int psci_cpu_init_idle(unsigned int cpu); int psci_cpu_suspend_enter(unsigned long index); struct psci_operations { + u32 (*get_version)(void); int (*cpu_suspend)(u32 state, unsigned long entry_point); int (*cpu_off)(u32 state); int (*cpu_on)(unsigned long cpuid, unsigned long entry_point); diff --git a/include/linux/refcount.h b/include/linux/refcount.h new file mode 100644 index 000000000000..600aadf9cca4 --- /dev/null +++ b/include/linux/refcount.h @@ -0,0 +1,294 @@ +#ifndef _LINUX_REFCOUNT_H +#define _LINUX_REFCOUNT_H + +/* + * Variant of atomic_t specialized for reference counts. + * + * The interface matches the atomic_t interface (to aid in porting) but only + * provides the few functions one should use for reference counting. + * + * It differs in that the counter saturates at UINT_MAX and will not move once + * there. This avoids wrapping the counter and causing 'spurious' + * use-after-free issues. + * + * Memory ordering rules are slightly relaxed wrt regular atomic_t functions + * and provide only what is strictly required for refcounts. + * + * The increments are fully relaxed; these will not provide ordering. The + * rationale is that whatever is used to obtain the object we're increasing the + * reference count on will provide the ordering. For locked data structures, + * its the lock acquire, for RCU/lockless data structures its the dependent + * load. + * + * Do note that inc_not_zero() provides a control dependency which will order + * future stores against the inc, this ensures we'll never modify the object + * if we did not in fact acquire a reference. + * + * The decrements will provide release order, such that all the prior loads and + * stores will be issued before, it also provides a control dependency, which + * will order us against the subsequent free(). + * + * The control dependency is against the load of the cmpxchg (ll/sc) that + * succeeded. This means the stores aren't fully ordered, but this is fine + * because the 1->0 transition indicates no concurrency. + * + * Note that the allocator is responsible for ordering things between free() + * and alloc(). + * + */ + +#include +#include +#include +#include + +#ifdef CONFIG_DEBUG_REFCOUNT +#define REFCOUNT_WARN(cond, str) WARN_ON(cond) +#define __refcount_check __must_check +#else +#define REFCOUNT_WARN(cond, str) (void)(cond) +#define __refcount_check +#endif + +typedef struct refcount_struct { + atomic_t refs; +} refcount_t; + +#define REFCOUNT_INIT(n) { .refs = ATOMIC_INIT(n), } + +static inline void refcount_set(refcount_t *r, unsigned int n) +{ + atomic_set(&r->refs, n); +} + +static inline unsigned int refcount_read(const refcount_t *r) +{ + return atomic_read(&r->refs); +} + +static inline __refcount_check +bool refcount_add_not_zero(unsigned int i, refcount_t *r) +{ + unsigned int old, new, val = atomic_read(&r->refs); + + for (;;) { + if (!val) + return false; + + if (unlikely(val == UINT_MAX)) + return true; + + new = val + i; + if (new < val) + new = UINT_MAX; + old = atomic_cmpxchg_relaxed(&r->refs, val, new); + if (old == val) + break; + + val = old; + } + + REFCOUNT_WARN(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n"); + + return true; +} + +static inline void refcount_add(unsigned int i, refcount_t *r) +{ + REFCOUNT_WARN(!refcount_add_not_zero(i, r), "refcount_t: addition on 0; use-after-free.\n"); +} + +/* + * Similar to atomic_inc_not_zero(), will saturate at UINT_MAX and WARN. + * + * Provides no memory ordering, it is assumed the caller has guaranteed the + * object memory to be stable (RCU, etc.). It does provide a control dependency + * and thereby orders future stores. See the comment on top. + */ +static inline __refcount_check +bool refcount_inc_not_zero(refcount_t *r) +{ + unsigned int old, new, val = atomic_read(&r->refs); + + for (;;) { + new = val + 1; + + if (!val) + return false; + + if (unlikely(!new)) + return true; + + old = atomic_cmpxchg_relaxed(&r->refs, val, new); + if (old == val) + break; + + val = old; + } + + REFCOUNT_WARN(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n"); + + return true; +} + +/* + * Similar to atomic_inc(), will saturate at UINT_MAX and WARN. + * + * Provides no memory ordering, it is assumed the caller already has a + * reference on the object, will WARN when this is not so. + */ +static inline void refcount_inc(refcount_t *r) +{ + REFCOUNT_WARN(!refcount_inc_not_zero(r), "refcount_t: increment on 0; use-after-free.\n"); +} + +/* + * Similar to atomic_dec_and_test(), it will WARN on underflow and fail to + * decrement when saturated at UINT_MAX. + * + * Provides release memory ordering, such that prior loads and stores are done + * before, and provides a control dependency such that free() must come after. + * See the comment on top. + */ +static inline __refcount_check +bool refcount_sub_and_test(unsigned int i, refcount_t *r) +{ + unsigned int old, new, val = atomic_read(&r->refs); + + for (;;) { + if (unlikely(val == UINT_MAX)) + return false; + + new = val - i; + if (new > val) { + REFCOUNT_WARN(new > val, "refcount_t: underflow; use-after-free.\n"); + return false; + } + + old = atomic_cmpxchg_release(&r->refs, val, new); + if (old == val) + break; + + val = old; + } + + return !new; +} + +static inline __refcount_check +bool refcount_dec_and_test(refcount_t *r) +{ + return refcount_sub_and_test(1, r); +} + +/* + * Similar to atomic_dec(), it will WARN on underflow and fail to decrement + * when saturated at UINT_MAX. + * + * Provides release memory ordering, such that prior loads and stores are done + * before. + */ +static inline +void refcount_dec(refcount_t *r) +{ + REFCOUNT_WARN(refcount_dec_and_test(r), "refcount_t: decrement hit 0; leaking memory.\n"); +} + +/* + * No atomic_t counterpart, it attempts a 1 -> 0 transition and returns the + * success thereof. + * + * Like all decrement operations, it provides release memory order and provides + * a control dependency. + * + * It can be used like a try-delete operator; this explicit case is provided + * and not cmpxchg in generic, because that would allow implementing unsafe + * operations. + */ +static inline __refcount_check +bool refcount_dec_if_one(refcount_t *r) +{ + return atomic_cmpxchg_release(&r->refs, 1, 0) == 1; +} + +/* + * No atomic_t counterpart, it decrements unless the value is 1, in which case + * it will return false. + * + * Was often done like: atomic_add_unless(&var, -1, 1) + */ +static inline __refcount_check +bool refcount_dec_not_one(refcount_t *r) +{ + unsigned int old, new, val = atomic_read(&r->refs); + + for (;;) { + if (unlikely(val == UINT_MAX)) + return true; + + if (val == 1) + return false; + + new = val - 1; + if (new > val) { + REFCOUNT_WARN(new > val, "refcount_t: underflow; use-after-free.\n"); + return true; + } + + old = atomic_cmpxchg_release(&r->refs, val, new); + if (old == val) + break; + + val = old; + } + + return true; +} + +/* + * Similar to atomic_dec_and_mutex_lock(), it will WARN on underflow and fail + * to decrement when saturated at UINT_MAX. + * + * Provides release memory ordering, such that prior loads and stores are done + * before, and provides a control dependency such that free() must come after. + * See the comment on top. + */ +static inline __refcount_check +bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock) +{ + if (refcount_dec_not_one(r)) + return false; + + mutex_lock(lock); + if (!refcount_dec_and_test(r)) { + mutex_unlock(lock); + return false; + } + + return true; +} + +/* + * Similar to atomic_dec_and_lock(), it will WARN on underflow and fail to + * decrement when saturated at UINT_MAX. + * + * Provides release memory ordering, such that prior loads and stores are done + * before, and provides a control dependency such that free() must come after. + * See the comment on top. + */ +static inline __refcount_check +bool refcount_dec_and_lock(refcount_t *r, spinlock_t *lock) +{ + if (refcount_dec_not_one(r)) + return false; + + spin_lock(lock); + if (!refcount_dec_and_test(r)) { + spin_unlock(lock); + return false; + } + + return true; +} + +#endif /* _LINUX_REFCOUNT_H */ diff --git a/include/linux/writeback.h b/include/linux/writeback.h index d0b5ca5d4e08..6c1cbbedc79c 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -224,6 +224,7 @@ static inline void inode_attach_wb(struct inode *inode, struct page *page) static inline void inode_detach_wb(struct inode *inode) { if (inode->i_wb) { + WARN_ON_ONCE(!(inode->i_state & I_CLEAR)); wb_put(inode->i_wb); inode->i_wb = NULL; } diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h index 0d43b19a9db0..ae8834d3fe54 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 @@ -13,12 +13,16 @@ #define _ICNSS_WLAN_H_ #include +#include #define ICNSS_MAX_IRQ_REGISTRATIONS 12 #define ICNSS_MAX_TIMESTAMP_LEN 32 +#ifndef ICNSS_API_WITH_DEV +#define ICNSS_API_WITH_DEV +#endif + enum icnss_uevent { - ICNSS_UEVENT_FW_READY, ICNSS_UEVENT_FW_CRASHED, ICNSS_UEVENT_FW_DOWN, }; @@ -40,6 +44,8 @@ struct icnss_uevent_data { struct icnss_driver_ops { char *name; + unsigned long drv_state; + struct device_driver driver; int (*probe)(struct device *dev); void (*remove)(struct device *dev); void (*shutdown)(struct device *dev); @@ -105,28 +111,34 @@ struct icnss_soc_info { char fw_build_timestamp[ICNSS_MAX_TIMESTAMP_LEN + 1]; }; -extern int icnss_register_driver(struct icnss_driver_ops *driver); -extern int icnss_unregister_driver(struct icnss_driver_ops *driver); -extern int icnss_wlan_enable(struct icnss_wlan_enable_cfg *config, +#define icnss_register_driver(ops) \ + __icnss_register_driver(ops, THIS_MODULE, KBUILD_MODNAME) +extern int __icnss_register_driver(struct icnss_driver_ops *ops, + struct module *owner, const char *mod_name); + +extern int icnss_unregister_driver(struct icnss_driver_ops *ops); + +extern int icnss_wlan_enable(struct device *dev, + struct icnss_wlan_enable_cfg *config, enum icnss_driver_mode mode, const char *host_version); -extern int icnss_wlan_disable(enum icnss_driver_mode mode); -extern void icnss_enable_irq(unsigned int ce_id); -extern void icnss_disable_irq(unsigned int ce_id); -extern int icnss_get_soc_info(struct icnss_soc_info *info); -extern int icnss_ce_free_irq(unsigned int ce_id, void *ctx); -extern int icnss_ce_request_irq(unsigned int ce_id, +extern int icnss_wlan_disable(struct device *dev, enum icnss_driver_mode mode); +extern void icnss_enable_irq(struct device *dev, unsigned int ce_id); +extern void icnss_disable_irq(struct device *dev, unsigned int ce_id); +extern int icnss_get_soc_info(struct device *dev, struct icnss_soc_info *info); +extern int icnss_ce_free_irq(struct device *dev, unsigned int ce_id, void *ctx); +extern int icnss_ce_request_irq(struct device *dev, unsigned int ce_id, irqreturn_t (*handler)(int, void *), unsigned long flags, const char *name, void *ctx); -extern int icnss_get_ce_id(int irq); -extern int icnss_set_fw_log_mode(uint8_t fw_log_mode); +extern int icnss_get_ce_id(struct device *dev, int irq); +extern int icnss_set_fw_log_mode(struct device *dev, uint8_t fw_log_mode); extern int icnss_athdiag_read(struct device *dev, uint32_t offset, uint32_t mem_type, uint32_t data_len, uint8_t *output); extern int icnss_athdiag_write(struct device *dev, uint32_t offset, uint32_t mem_type, uint32_t data_len, uint8_t *input); -extern int icnss_get_irq(int ce_id); +extern int icnss_get_irq(struct device *dev, int ce_id); extern int icnss_power_on(struct device *dev); extern int icnss_power_off(struct device *dev); extern struct dma_iommu_mapping *icnss_smmu_get_mapping(struct device *dev); @@ -138,7 +150,7 @@ extern int icnss_get_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 *ch_count, u16 buf_len); extern int icnss_wlan_set_dfs_nol(const void *info, u16 info_len); extern int icnss_wlan_get_dfs_nol(void *info, u16 info_len); -extern bool icnss_is_qmi_disable(void); +extern bool icnss_is_qmi_disable(struct device *dev); extern bool icnss_is_fw_ready(void); extern bool icnss_is_fw_down(void); extern int icnss_set_wlan_mac_address(const u8 *in, const uint32_t len); diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h index 3b91ad5d5115..2dd096eea935 100644 --- a/include/sound/rawmidi.h +++ b/include/sound/rawmidi.h @@ -78,6 +78,7 @@ struct snd_rawmidi_runtime { size_t xruns; /* over/underruns counter */ /* misc */ spinlock_t lock; + struct mutex realloc_mutex; wait_queue_head_t sleep; /* event handler (new bytes, input only) */ void (*event)(struct snd_rawmidi_substream *substream); diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 0630e0f64b9c..a73de08c6584 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -80,6 +80,26 @@ extern "C" { #define DRM_MODE_FLAG_SUPPORTS_RGB (1<<20) #define DRM_MODE_FLAG_SUPPORTS_YUV (1<<21) +/* Picture aspect ratio options */ +#define DRM_MODE_PICTURE_ASPECT_NONE 0 +#define DRM_MODE_PICTURE_ASPECT_4_3 1 +#define DRM_MODE_PICTURE_ASPECT_16_9 2 +#define DRM_MODE_PICTURE_ASPECT_64_27 3 +#define DRM_MODE_PICTURE_ASPECT_256_135 4 + +/* Aspect ratio flag bitmask (4 bits 27:24) */ +#define DRM_MODE_FLAG_PIC_AR_MASK (0x0F<<24) +#define DRM_MODE_FLAG_PIC_AR_NONE \ + (DRM_MODE_PICTURE_ASPECT_NONE<<24) +#define DRM_MODE_FLAG_PIC_AR_4_3 \ + (DRM_MODE_PICTURE_ASPECT_4_3<<24) +#define DRM_MODE_FLAG_PIC_AR_16_9 \ + (DRM_MODE_PICTURE_ASPECT_16_9<<24) +#define DRM_MODE_FLAG_PIC_AR_64_27 \ + (DRM_MODE_PICTURE_ASPECT_64_27<<24) +#define DRM_MODE_FLAG_PIC_AR_256_135 \ + (DRM_MODE_PICTURE_ASPECT_256_135<<24) + /* DPMS flags */ /* bit compatible with the xorg definitions. */ #define DRM_MODE_DPMS_ON 0 @@ -94,11 +114,6 @@ extern "C" { #define DRM_MODE_SCALE_CENTER 2 /* Centered, no scaling */ #define DRM_MODE_SCALE_ASPECT 3 /* Full screen, preserve aspect */ -/* Picture aspect ratio options */ -#define DRM_MODE_PICTURE_ASPECT_NONE 0 -#define DRM_MODE_PICTURE_ASPECT_4_3 1 -#define DRM_MODE_PICTURE_ASPECT_16_9 2 - /* Dithering mode options */ #define DRM_MODE_DITHERING_OFF 0 #define DRM_MODE_DITHERING_ON 1 diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h index 4d0b992d0ba6..3ca37a910218 100644 --- a/include/uapi/linux/msm_ipa.h +++ b/include/uapi/linux/msm_ipa.h @@ -435,7 +435,13 @@ enum ipa_ssr_event { IPA_SSR_EVENT_MAX }; -#define IPA_EVENT_MAX_NUM ((int)IPA_SSR_EVENT_MAX) +enum ipa_wlan_fw_ssr_event { + WLAN_FWR_SSR_BEFORE_SHUTDOWN = IPA_SSR_EVENT_MAX, + IPA_WLAN_FW_SSR_EVENT_MAX, +#define IPA_WLAN_FW_SSR_EVENT_MAX IPA_WLAN_FW_SSR_EVENT_MAX +}; + +#define IPA_EVENT_MAX_NUM ((int)IPA_WLAN_FW_SSR_EVENT_MAX) /** * enum ipa_rm_resource_name - IPA RM clients identification names diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index d3ab3f57aa9f..b6c14b1ebdaf 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -717,6 +717,7 @@ struct usb_interface_assoc_descriptor { __u8 iFunction; } __attribute__ ((packed)); +#define USB_DT_INTERFACE_ASSOCIATION_SIZE 8 /*-------------------------------------------------------------------------*/ diff --git a/include/uapi/media/msm_cam_sensor.h b/include/uapi/media/msm_cam_sensor.h index 0ec18d663cff..e4b4554dd690 100644 --- a/include/uapi/media/msm_cam_sensor.h +++ b/include/uapi/media/msm_cam_sensor.h @@ -39,6 +39,8 @@ #define MSM_V4L2_PIX_FMT_META v4l2_fourcc('M', 'E', 'T', 'A') /* META */ #define MSM_V4L2_PIX_FMT_META10 v4l2_fourcc('M', 'E', '1', '0') /* META10 */ +#define MSM_V4L2_PIX_FMT_META12 v4l2_fourcc('M', 'E', '1', '2') /* META12 */ + #define MSM_V4L2_PIX_FMT_SBGGR14 v4l2_fourcc('B', 'G', '1', '4') /* 14 BGBG.. GRGR.. */ #define MSM_V4L2_PIX_FMT_SGBRG14 v4l2_fourcc('G', 'B', '1', '4') diff --git a/include/uapi/media/msm_camera.h b/include/uapi/media/msm_camera.h index 39e6927d9b7e..81e350ede6eb 100644 --- a/include/uapi/media/msm_camera.h +++ b/include/uapi/media/msm_camera.h @@ -1,4 +1,5 @@ -/* Copyright (c) 2009-2012, 2014-2016 The Linux Foundation. All rights reserved. +/* Copyright (c) 2009-2012, 2014-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 @@ -262,7 +263,7 @@ struct msm_mctl_post_proc_cmd { #define MSM_CAMERA_STROBE_FLASH_NONE 0 #define MSM_CAMERA_STROBE_FLASH_XENON 1 -#define MSM_MAX_CAMERA_SENSORS 5 +#define MSM_MAX_CAMERA_SENSORS 6 #define MAX_SENSOR_NAME 32 #define MAX_CAM_NAME_SIZE 32 #define MAX_ACT_MOD_NAME_SIZE 32 @@ -1386,6 +1387,7 @@ struct msm_camera_csiphy_params { uint16_t lane_mask; uint8_t combo_mode; uint8_t csid_core; + uint64_t data_rate; }; struct msm_camera_csi2_params { diff --git a/include/uapi/media/msm_camsensor_sdk.h b/include/uapi/media/msm_camsensor_sdk.h index 8e762a9cee3d..d35124c5a154 100755 --- a/include/uapi/media/msm_camsensor_sdk.h +++ b/include/uapi/media/msm_camsensor_sdk.h @@ -50,6 +50,8 @@ #define MSM_SENSOR_BYPASS_VIDEO_NODE 1 +#define FRONT_AUX_SENSOR_SUPPORT + enum msm_sensor_camera_id_t { CAMERA_0, CAMERA_1, @@ -70,6 +72,7 @@ enum camb_position_t { BACK_CAMERA_B, FRONT_CAMERA_B, AUX_CAMERA_B = 0x100, + FRONT_AUX_CAMERA_B, INVALID_CAMERA_B, }; @@ -394,6 +397,7 @@ struct msm_camera_csiphy_params { unsigned char csid_core; unsigned int csiphy_clk; unsigned char csi_3phase; + uint64_t data_rate; }; struct msm_camera_i2c_seq_reg_array { diff --git a/include/uapi/media/msmb_camera.h b/include/uapi/media/msmb_camera.h index 4b23806071d4..6341b25e416a 100644 --- a/include/uapi/media/msmb_camera.h +++ b/include/uapi/media/msmb_camera.h @@ -53,7 +53,7 @@ #define MSM_CAMERA_SUBDEV_EXT 19 #define MSM_CAMERA_SUBDEV_TOF 20 #define MSM_CAMERA_SUBDEV_LASER_LED 21 -#define MSM_MAX_CAMERA_SENSORS 5 +#define MSM_MAX_CAMERA_SENSORS 6 /* The below macro is defined to put an upper limit on maximum * number of buffer requested per stream. In case of extremely diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 3fdb7545852e..cd3d81961cc2 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -4092,7 +4092,11 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from) */ 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/futex.c b/kernel/futex.c index af29863f3349..760a97da1050 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -470,6 +470,7 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw) unsigned long address = (unsigned long)uaddr; struct mm_struct *mm = current->mm; struct page *page, *page_head; + struct address_space *mapping; int err, ro = 0; /* @@ -555,7 +556,19 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw) } #endif - lock_page(page_head); + /* + * The treatment of mapping from this point on is critical. The page + * lock protects many things but in this context the page lock + * stabilizes mapping, prevents inode freeing in the shared + * file-backed region case and guards against movement to swap cache. + * + * Strictly speaking the page lock is not needed in all cases being + * considered here and page lock forces unnecessarily serialization + * From this point on, mapping will be re-verified if necessary and + * page lock will be acquired only if it is unavoidable + */ + + mapping = READ_ONCE(page_head->mapping); /* * If page_head->mapping is NULL, then it cannot be a PageAnon @@ -572,18 +585,31 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw) * shmem_writepage move it from filecache to swapcache beneath us: * an unlikely race, but we do need to retry for page_head->mapping. */ - if (!page_head->mapping) { - int shmem_swizzled = PageSwapCache(page_head); + if (unlikely(!mapping)) { + int shmem_swizzled; + + /* + * Page lock is required to identify which special case above + * applies. If this is really a shmem page then the page lock + * will prevent unexpected transitions. + */ + lock_page(page); + shmem_swizzled = PageSwapCache(page) || page->mapping; unlock_page(page_head); put_page(page_head); + if (shmem_swizzled) goto again; + return -EFAULT; } /* * Private mappings are handled in a simple way. * + * If the futex key is stored on an anonymous page, then the associated + * object is the mm which is implicitly pinned by the calling process. + * * NOTE: When userspace waits on a MAP_SHARED mapping, even if * it's a read-only handle, it's expected that futexes attach to * the object not the particular process. @@ -601,16 +627,74 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key, int rw) key->both.offset |= FUT_OFF_MMSHARED; /* ref taken on mm */ key->private.mm = mm; key->private.address = address; + + get_futex_key_refs(key); /* implies smp_mb(); (B) */ + } else { + struct inode *inode; + + /* + * The associated futex object in this case is the inode and + * the page->mapping must be traversed. Ordinarily this should + * be stabilised under page lock but it's not strictly + * necessary in this case as we just want to pin the inode, not + * update the radix tree or anything like that. + * + * The RCU read lock is taken as the inode is finally freed + * under RCU. If the mapping still matches expectations then the + * mapping->host can be safely accessed as being a valid inode. + */ + rcu_read_lock(); + + if (READ_ONCE(page_head->mapping) != mapping) { + rcu_read_unlock(); + put_page(page_head); + + goto again; + } + + inode = READ_ONCE(mapping->host); + if (!inode) { + rcu_read_unlock(); + put_page(page_head); + + goto again; + } + + /* + * Take a reference unless it is about to be freed. Previously + * this reference was taken by ihold under the page lock + * pinning the inode in place so i_lock was unnecessary. The + * only way for this check to fail is if the inode was + * truncated in parallel so warn for now if this happens. + * + * We are not calling into get_futex_key_refs() in file-backed + * cases, therefore a successful atomic_inc return below will + * guarantee that get_futex_key() will still imply smp_mb(); (B). + */ + if (WARN_ON_ONCE(!atomic_inc_not_zero(&inode->i_count))) { + rcu_read_unlock(); + put_page(page_head); + + goto again; + } + + /* Should be impossible but lets be paranoid for now */ + if (WARN_ON_ONCE(inode->i_mapping != mapping)) { + err = -EFAULT; + rcu_read_unlock(); + iput(inode); + + goto out; + } + key->both.offset |= FUT_OFF_INODE; /* inode-based key */ - key->shared.inode = page_head->mapping->host; + key->shared.inode = inode; key->shared.pgoff = basepage_index(page); + rcu_read_unlock(); } - get_futex_key_refs(key); /* implies MB (B) */ - out: - unlock_page(page_head); put_page(page_head); return err; } @@ -1621,6 +1705,9 @@ static int futex_requeue(u32 __user *uaddr1, unsigned int flags, struct futex_q *this, *next; WAKE_Q(wake_q); + if (nr_wake < 0 || nr_requeue < 0) + return -EINVAL; + if (requeue_pi) { /* * Requeue PI only works on two distinct uaddrs. This @@ -1939,8 +2026,12 @@ static int unqueue_me(struct futex_q *q) /* In the common case we don't take the spinlock, which is nice. */ retry: - lock_ptr = q->lock_ptr; - barrier(); + /* + * q->lock_ptr can change between this read and the following spin_lock. + * Use READ_ONCE to forbid the compiler from reloading q->lock_ptr and + * optimizing lock_ptr out of the logic below. + */ + lock_ptr = READ_ONCE(q->lock_ptr); if (lock_ptr != NULL) { spin_lock(lock_ptr); /* diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 422438d43d90..642520e1274c 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6535,17 +6535,19 @@ long group_norm_util(struct energy_env *eenv, struct sched_group *sg) static int find_new_capacity(struct energy_env *eenv, const struct sched_group_energy * const sge) { - int idx; + int idx, max_idx = sge->nr_cap_states - 1; unsigned long util = group_max_util(eenv); + /* default is max_cap if we don't find a match */ + eenv->cap_idx = max_idx; + for (idx = 0; idx < sge->nr_cap_states; idx++) { if (sge->cap_states[idx].cap >= util) + eenv->cap_idx = idx; break; } - eenv->cap_idx = idx; - - return idx; + return eenv->cap_idx; } static int group_idle_state(struct sched_group *sg) diff --git a/kernel/sched/hmp.c b/kernel/sched/hmp.c index 70a556f0dd06..53ffa09bf22e 100644 --- a/kernel/sched/hmp.c +++ b/kernel/sched/hmp.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 @@ -2880,11 +2880,15 @@ void update_task_ravg(struct task_struct *p, struct rq *rq, int event, update_task_burst(p, rq, event, runtime); update_cpu_busy_time(p, rq, event, wallclock, irqtime); update_task_pred_demand(rq, p, event); -done: + + if (exiting_task(p)) + goto done; + trace_sched_update_task_ravg(p, rq, event, wallclock, irqtime, rq->cc.cycles, rq->cc.time, p->grp ? &rq->grp_time : NULL); +done: p->ravg.mark_start = wallclock; } diff --git a/kernel/taskstats.c b/kernel/taskstats.c index 21f82c29c914..11cc757795cd 100644 --- a/kernel/taskstats.c +++ b/kernel/taskstats.c @@ -54,7 +54,11 @@ static const struct nla_policy taskstats_cmd_get_policy[TASKSTATS_CMD_ATTR_MAX+1 [TASKSTATS_CMD_ATTR_REGISTER_CPUMASK] = { .type = NLA_STRING }, [TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK] = { .type = NLA_STRING },}; -static const struct nla_policy cgroupstats_cmd_get_policy[CGROUPSTATS_CMD_ATTR_MAX+1] = { +/* + * We have to use TASKSTATS_CMD_ATTR_MAX here, it is the maxattr in the family. + * Make sure they are always aligned. + */ +static const struct nla_policy cgroupstats_cmd_get_policy[TASKSTATS_CMD_ATTR_MAX+1] = { [CGROUPSTATS_CMD_ATTR_FD] = { .type = NLA_U32 }, }; diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index e7c2392666cb..af27996c6a23 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -669,7 +669,9 @@ static void hrtimer_reprogram(struct hrtimer *timer, static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base) { base->expires_next.tv64 = KTIME_MAX; + base->hang_detected = 0; base->hres_active = 0; + base->next_timer = NULL; } /* @@ -1587,6 +1589,7 @@ static void init_hrtimers_cpu(int cpu) timerqueue_init_head(&cpu_base->clock_base[i].active); } + cpu_base->active_bases = 0; cpu_base->cpu = cpu; hrtimer_init_hres(cpu_base); } diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 8eb4b826da6d..31f244b5c0a3 100755 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -682,6 +682,19 @@ source "lib/Kconfig.kmemcheck" source "lib/Kconfig.kasan" +config DEBUG_REFCOUNT + bool "Verbose refcount checks" + help + Say Y here if you want reference counters (refcount_t and kref) to + generate WARNs on dubious usage. Without this refcount_t will still + be a saturating counter and avoid Use-After-Free by turning it into + a resource leak Denial-Of-Service. + + Use of this option will increase kernel text size but will alert the + admin of potential abuse. + + If in doubt, say "N". + endmenu # "Memory Debugging" config DEBUG_SHIRQ diff --git a/lib/asn1_decoder.c b/lib/asn1_decoder.c index 554522934c44..762d152f7358 100644 --- a/lib/asn1_decoder.c +++ b/lib/asn1_decoder.c @@ -283,6 +283,9 @@ int asn1_ber_decoder(const struct asn1_decoder *decoder, if (unlikely(len > datalen - dp)) goto data_overrun_error; } + } else { + if (unlikely(len > datalen - dp)) + goto data_overrun_error; } if (flags & FLAG_CONS) { @@ -309,42 +312,47 @@ int asn1_ber_decoder(const struct asn1_decoder *decoder, /* Decide how to handle the operation */ switch (op) { - case ASN1_OP_MATCH_ANY_ACT: - case ASN1_OP_MATCH_ANY_ACT_OR_SKIP: - case ASN1_OP_COND_MATCH_ANY_ACT: - case ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP: - ret = actions[machine[pc + 1]](context, hdr, tag, data + dp, len); - if (ret < 0) - return ret; - goto skip_data; - - case ASN1_OP_MATCH_ACT: - case ASN1_OP_MATCH_ACT_OR_SKIP: - case ASN1_OP_COND_MATCH_ACT_OR_SKIP: - ret = actions[machine[pc + 2]](context, hdr, tag, data + dp, len); - if (ret < 0) - return ret; - goto skip_data; - case ASN1_OP_MATCH: case ASN1_OP_MATCH_OR_SKIP: + case ASN1_OP_MATCH_ACT: + case ASN1_OP_MATCH_ACT_OR_SKIP: case ASN1_OP_MATCH_ANY: case ASN1_OP_MATCH_ANY_OR_SKIP: + case ASN1_OP_MATCH_ANY_ACT: + case ASN1_OP_MATCH_ANY_ACT_OR_SKIP: case ASN1_OP_COND_MATCH_OR_SKIP: + case ASN1_OP_COND_MATCH_ACT_OR_SKIP: case ASN1_OP_COND_MATCH_ANY: case ASN1_OP_COND_MATCH_ANY_OR_SKIP: - skip_data: + case ASN1_OP_COND_MATCH_ANY_ACT: + case ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP: + if (!(flags & FLAG_CONS)) { if (flags & FLAG_INDEFINITE_LENGTH) { + size_t tmp = dp; + ret = asn1_find_indefinite_length( - data, datalen, &dp, &len, &errmsg); + data, datalen, &tmp, &len, &errmsg); if (ret < 0) goto error; - } else { - dp += len; } pr_debug("- LEAF: %zu\n", len); } + + if (op & ASN1_OP_MATCH__ACT) { + unsigned char act; + + if (op & ASN1_OP_MATCH__ANY) + act = machine[pc + 1]; + else + act = machine[pc + 2]; + ret = actions[act](context, hdr, tag, data + dp, len); + if (ret < 0) + return ret; + } + + if (!(flags & FLAG_CONS)) + dp += len; pc += asn1_op_lengths[op]; goto next_op; diff --git a/mm/migrate.c b/mm/migrate.c index 85af2816b6d2..a021071eceaf 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -40,6 +40,7 @@ #include #include #include +#include #include @@ -1649,7 +1650,6 @@ SYSCALL_DEFINE6(move_pages, pid_t, pid, unsigned long, nr_pages, const int __user *, nodes, int __user *, status, int, flags) { - const struct cred *cred = current_cred(), *tcred; struct task_struct *task; struct mm_struct *mm; int err; @@ -1673,14 +1673,9 @@ SYSCALL_DEFINE6(move_pages, pid_t, pid, unsigned long, nr_pages, /* * Check if this process has the right to modify the specified - * process. The right exists if the process has administrative - * capabilities, superuser privileges or the same - * userid as the target process. + * process. Use the regular "ptrace_may_access()" checks. */ - tcred = __task_cred(task); - if (!uid_eq(cred->euid, tcred->suid) && !uid_eq(cred->euid, tcred->uid) && - !uid_eq(cred->uid, tcred->suid) && !uid_eq(cred->uid, tcred->uid) && - !capable(CAP_SYS_NICE)) { + if (!ptrace_may_access(task, PTRACE_MODE_READ_REALCREDS)) { rcu_read_unlock(); err = -EPERM; goto out; diff --git a/mm/mmap.c b/mm/mmap.c index 16743bf76a88..4afefe5df4bc 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -205,6 +205,13 @@ int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin) */ free += global_page_state(NR_SLAB_RECLAIMABLE); + /* + * Part of the kernel memory, which can be released + * under memory pressure. + */ + free += global_page_state( + NR_INDIRECTLY_RECLAIMABLE_BYTES) >> PAGE_SHIFT; + /* * Leave reserved pages. The pages are not for anonymous pages. */ diff --git a/mm/nommu.c b/mm/nommu.c index 92be862c859b..8d75e425c21c 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -1879,6 +1879,13 @@ int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin) */ free += global_page_state(NR_SLAB_RECLAIMABLE); + /* + * Part of the kernel memory, which can be released + * under memory pressure. + */ + free += global_page_state( + NR_INDIRECTLY_RECLAIMABLE_BYTES) >> PAGE_SHIFT; + /* * Leave reserved pages. The pages are not for anonymous pages. */ diff --git a/mm/vmstat.c b/mm/vmstat.c index 3c0796cd3f80..23f7d1bc2c5b 100644 --- a/mm/vmstat.c +++ b/mm/vmstat.c @@ -764,6 +764,7 @@ const char * const vmstat_text[] = { "nr_anon_transparent_hugepages", "nr_free_cma", "nr_swapcache", + "nr_indirectly_reclaimable", /* enum writeback_stat_item counters */ "nr_dirty_threshold", diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index f02ffe558a08..408a0ecdbf89 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -430,8 +430,8 @@ static void hidp_del_timer(struct hidp_session *session) del_timer(&session->timer); } -static void hidp_process_report(struct hidp_session *session, - int type, const u8 *data, int len, int intr) +static void hidp_process_report(struct hidp_session *session, int type, + const u8 *data, unsigned int len, int intr) { if (len > HID_MAX_BUFFER_SIZE) len = HID_MAX_BUFFER_SIZE; diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index f46ca417bf2d..50b76011f470 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -2021,7 +2021,9 @@ static int ebt_size_mwt(struct compat_ebt_entry_mwt *match32, if (match_kern) match_kern->match_size = ret; - WARN_ON(type == EBT_COMPAT_TARGET && size_left); + if (WARN_ON(type == EBT_COMPAT_TARGET && size_left)) + return -EINVAL; + match32 = (struct compat_ebt_entry_mwt *) buf; } @@ -2078,6 +2080,15 @@ static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base, * * offsets are relative to beginning of struct ebt_entry (i.e., 0). */ + for (i = 0; i < 4 ; ++i) { + if (offsets[i] >= *total) + return -EINVAL; + if (i == 0) + continue; + if (offsets[i-1] > offsets[i]) + return -EINVAL; + } + for (i = 0, j = 1 ; j < 4 ; j++, i++) { struct compat_ebt_entry_mwt *match32; unsigned int size; diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 2e9a1c2818c7..967f0a4b2f63 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -261,7 +261,7 @@ struct net *get_net_ns_by_id(struct net *net, int id) 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/sockev_nlmcast.c b/net/core/sockev_nlmcast.c index 749ffb81c87c..22148bf76e0a 100644 --- a/net/core/sockev_nlmcast.c +++ b/net/core/sockev_nlmcast.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-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 @@ -36,6 +36,7 @@ static struct netlink_kernel_cfg nlcfg = { static void _sockev_event(unsigned long event, __u8 *evstr, int buflen) { + switch (event) { case SOCKEV_SOCKET: strlcpy(evstr, "SOCKEV_SOCKET", buflen); @@ -94,6 +95,7 @@ static int sockev_client_cb(struct notifier_block *nb, NETLINK_CB(skb).dst_group = SKNLGRP_SOCKEV; smsg = nlmsg_data(nlh); + memset(smsg, 0, sizeof(struct sknlsockevmsg)); smsg->pid = current->pid; _sockev_event(event, smsg->event, sizeof(smsg->event)); smsg->skfamily = sock->sk->sk_family; diff --git a/net/ipc_router/ipc_router_core.c b/net/ipc_router/ipc_router_core.c index d4b1d9a197f5..bfb76a84be73 100644 --- a/net/ipc_router/ipc_router_core.c +++ b/net/ipc_router/ipc_router_core.c @@ -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 @@ -224,6 +224,25 @@ void msm_ipc_router_set_ws_allowed(bool flag) is_wakeup_source_allowed = flag; } +/** + * is_sensor_port() - Check if the remote port is sensor service or not + * @rport: Pointer to the remote port. + * + * Return: true if the remote port is sensor service else false. + */ +static int is_sensor_port(struct msm_ipc_router_remote_port *rport) +{ + u32 svcid = 0; + + if (rport && rport->server) { + svcid = rport->server->name.service; + if (svcid == 400 || (svcid >= 256 && svcid <= 320)) + return true; + } + + return false; +} + static void init_routing_table(void) { int i; @@ -277,7 +296,7 @@ static uint32_t ipc_router_calc_checksum(union rr_control_msg *msg) */ static void skb_copy_to_log_buf(struct sk_buff_head *skb_head, unsigned int pl_len, unsigned int hdr_offset, - uint64_t *log_buf) + unsigned char *log_buf) { struct sk_buff *temp_skb; unsigned int copied_len = 0, copy_len = 0; @@ -357,7 +376,8 @@ static void ipc_router_log_msg(void *log_ctx, uint32_t xchng_type, else if (hdr->version == IPC_ROUTER_V2) hdr_offset = sizeof(struct rr_header_v2); } - skb_copy_to_log_buf(skb_head, buf_len, hdr_offset, &pl_buf); + skb_copy_to_log_buf(skb_head, buf_len, hdr_offset, + (unsigned char *)&pl_buf); if (port_ptr && rport_ptr && (port_ptr->type == CLIENT_PORT) && (rport_ptr->server != NULL)) { @@ -2730,7 +2750,6 @@ static void do_read_data(struct work_struct *work) struct rr_packet *pkt = NULL; struct msm_ipc_port *port_ptr; struct msm_ipc_router_remote_port *rport_ptr; - int ret; struct msm_ipc_router_xprt_info *xprt_info = container_of(work, @@ -2738,16 +2757,7 @@ static void do_read_data(struct work_struct *work) read_data); while ((pkt = rr_read(xprt_info)) != NULL) { - if (pkt->length < calc_rx_header_size(xprt_info) || - pkt->length > MAX_IPC_PKT_SIZE) { - IPC_RTR_ERR("%s: Invalid pkt length %d\n", - __func__, pkt->length); - goto read_next_pkt1; - } - ret = extract_header(pkt); - if (ret < 0) - goto read_next_pkt1; hdr = &(pkt->hdr); if ((hdr->dst_node_id != IPC_ROUTER_NID_LOCAL) && @@ -4194,6 +4204,7 @@ void msm_ipc_router_xprt_notify(struct msm_ipc_router_xprt *xprt, { struct msm_ipc_router_xprt_info *xprt_info = xprt->priv; struct msm_ipc_router_xprt_work *xprt_work; + struct msm_ipc_router_remote_port *rport_ptr = NULL; struct rr_packet *pkt; int ret; @@ -4246,16 +4257,40 @@ void msm_ipc_router_xprt_notify(struct msm_ipc_router_xprt *xprt, if (!pkt) return; + if (pkt->length < calc_rx_header_size(xprt_info) || + pkt->length > MAX_IPC_PKT_SIZE) { + IPC_RTR_ERR("%s: Invalid pkt length %d\n", + __func__, pkt->length); + release_pkt(pkt); + return; + } + + ret = extract_header(pkt); + if (ret < 0) { + release_pkt(pkt); + return; + } + pkt->ws_need = false; + + if (pkt->hdr.type == IPC_ROUTER_CTRL_CMD_DATA) + rport_ptr = ipc_router_get_rport_ref(pkt->hdr.src_node_id, + pkt->hdr.src_port_id); + mutex_lock(&xprt_info->rx_lock_lhb2); list_add_tail(&pkt->list, &xprt_info->pkt_list); - if (!xprt_info->dynamic_ws) { - __pm_stay_awake(&xprt_info->ws); - pkt->ws_need = true; - } else { - if (is_wakeup_source_allowed) { + /* check every pkt is from SENSOR services or not and + * avoid holding both edge and port specific wake-up sources + */ + if (!is_sensor_port(rport_ptr)) { + if (!xprt_info->dynamic_ws) { __pm_stay_awake(&xprt_info->ws); pkt->ws_need = true; + } else { + if (is_wakeup_source_allowed) { + __pm_stay_awake(&xprt_info->ws); + pkt->ws_need = true; + } } } mutex_unlock(&xprt_info->rx_lock_lhb2); diff --git a/net/ipc_router/ipc_router_socket.c b/net/ipc_router/ipc_router_socket.c index a87514f6310a..7c82f7ff8874 100644 --- a/net/ipc_router/ipc_router_socket.c +++ b/net/ipc_router/ipc_router_socket.c @@ -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 @@ -143,6 +143,7 @@ static int msm_ipc_router_extract_msg(struct msghdr *m, return -EINVAL; } ctl_msg = (union rr_control_msg *)(temp->data); + memset(addr, 0x0, sizeof(*addr)); addr->family = AF_MSM_IPC; addr->address.addrtype = MSM_IPC_ADDR_ID; addr->address.addr.port_addr.node_id = ctl_msg->cli.node_id; @@ -151,6 +152,7 @@ static int msm_ipc_router_extract_msg(struct msghdr *m, return offset; } if (addr && (hdr->type == IPC_ROUTER_CTRL_CMD_DATA)) { + memset(addr, 0x0, sizeof(*addr)); addr->family = AF_MSM_IPC; addr->address.addrtype = MSM_IPC_ADDR_ID; addr->address.addr.port_addr.node_id = hdr->src_node_id; diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 661bda968594..f04b33114b97 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -922,10 +922,12 @@ static int __ip_append_data(struct sock *sk, csummode = CHECKSUM_PARTIAL; cork->length += length; - if (((length > mtu) || (skb && skb_is_gso(skb))) && + if ((skb && skb_is_gso(skb)) || + (((length + (skb ? skb->len : fragheaderlen)) > mtu) && + (skb_queue_len(queue) <= 1) && (sk->sk_protocol == IPPROTO_UDP) && - (rt->dst.dev->features & NETIF_F_UFO) && !rt->dst.header_len && - (sk->sk_type == SOCK_DGRAM) && !sk->sk_no_check_tx) { + (rt->dst.dev->features & NETIF_F_UFO) && !dst_xfrm(&rt->dst) && + (sk->sk_type == SOCK_DGRAM) && !sk->sk_no_check_tx)) { err = ip_ufo_append_data(sk, queue, getfrag, from, length, hh_len, fragheaderlen, transhdrlen, maxfraglen, flags); @@ -1241,6 +1243,7 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, return -EINVAL; if ((size + skb->len > mtu) && + (skb_queue_len(&sk->sk_write_queue) == 1) && (sk->sk_protocol == IPPROTO_UDP) && (rt->dst.dev->features & NETIF_F_UFO)) { if (skb->ip_summed != CHECKSUM_PARTIAL) diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 6e3e0e8b1ce3..cff58f7ee559 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -329,6 +329,10 @@ unsigned int arpt_do_table(struct sk_buff *skb, } if (table_base + v != arpt_next_entry(e)) { + if (unlikely(stackidx >= private->stacksize)) { + verdict = NF_DROP; + break; + } jumpstack[stackidx++] = e; } diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index a399c5419622..849833482390 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -408,9 +408,11 @@ ipt_do_table(struct sk_buff *skb, } if (table_base + v != ipt_next_entry(e) && !(e->ip.flags & IPT_F_GOTO)) { + if (unlikely(stackidx >= private->stacksize)) { + verdict = NF_DROP; + break; + } jumpstack[stackidx++] = e; - pr_debug("Pushed %p into pos %u\n", - e, stackidx - 1); } e = get_entry(table_base, v); diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index ca1031411aa7..7541427537d0 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -500,11 +500,16 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) 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. */ @@ -579,7 +584,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) /* 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) @@ -601,9 +606,9 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) 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 (!saddr && ipc.oif) { @@ -612,7 +617,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) goto done; } - if (!inet->hdrincl) { + if (!hdrincl) { rfv.msg = msg; rfv.hlen = 0; @@ -637,7 +642,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) goto do_confirm; back_from_confirm: - if (inet->hdrincl) + if (hdrincl) err = raw_send_hdrinc(sk, &fl4, msg, len, &rt, msg->msg_flags); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 254fcc7f1825..4d6f09c05a12 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -824,7 +824,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4) if (is_udplite) /* UDP-Lite */ csum = udplite_csum(skb); - else if (sk->sk_no_check_tx) { /* UDP csum disabled */ + else if (sk->sk_no_check_tx && !skb_is_gso(skb)) { /* UDP csum off */ skb->ip_summed = CHECKSUM_NONE; goto send; diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 72b53b83a720..9921363e1feb 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1241,14 +1241,16 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork, v6_cork->tclass = tclass; if (rt->dst.flags & DST_XFRM_TUNNEL) mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ? - rt->dst.dev->mtu : dst_mtu(&rt->dst); + READ_ONCE(rt->dst.dev->mtu) : dst_mtu(&rt->dst); else mtu = np->pmtudisc >= IPV6_PMTUDISC_PROBE ? - rt->dst.dev->mtu : dst_mtu(rt->dst.path); + READ_ONCE(rt->dst.dev->mtu) : dst_mtu(rt->dst.path); if (np->frag_size < mtu) { if (np->frag_size) mtu = np->frag_size; } + if (mtu < IPV6_MIN_MTU) + return -EINVAL; cork->base.fragsize = mtu; if (dst_allfrag(rt->dst.path)) cork->base.flags |= IPCORK_ALLFRAG; @@ -1360,11 +1362,12 @@ static int __ip6_append_data(struct sock *sk, */ cork->length += length; - if (((length > mtu) || - (skb && skb_is_gso(skb))) && + if ((skb && skb_is_gso(skb)) || + (((length + (skb ? skb->len : headersize)) > mtu) && + (skb_queue_len(queue) <= 1) && (sk->sk_protocol == IPPROTO_UDP) && - (rt->dst.dev->features & NETIF_F_UFO) && - (sk->sk_type == SOCK_DGRAM) && !udp_get_no_check6_tx(sk)) { + (rt->dst.dev->features & NETIF_F_UFO) && !dst_xfrm(&rt->dst) && + (sk->sk_type == SOCK_DGRAM) && !udp_get_no_check6_tx(sk))) { err = ip6_ufo_append_data(sk, queue, getfrag, from, length, hh_len, fragheaderlen, exthdrlen, transhdrlen, mtu, flags, fl6); @@ -1777,11 +1780,13 @@ struct sk_buff *ip6_make_skb(struct sock *sk, cork.base.flags = 0; cork.base.addr = 0; cork.base.opt = NULL; + cork.base.dst = NULL; v6_cork.opt = NULL; err = ip6_setup_cork(sk, &cork, &v6_cork, hlimit, tclass, opt, rt, fl6); - if (err) + if (err) { + ip6_cork_release(&cork, &v6_cork); return ERR_PTR(err); - + } if (dontfrag < 0) dontfrag = inet6_sk(sk)->dontfrag; diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 6fd784643d6e..3d616c6733e5 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -429,6 +429,10 @@ ip6t_do_table(struct sk_buff *skb, } if (table_base + v != ip6t_next_entry(e) && !(e->ipv6.flags & IP6T_F_GOTO)) { + if (unlikely(stackidx >= private->stacksize)) { + verdict = NF_DROP; + break; + } jumpstack[stackidx++] = e; } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index dd37fe0b6a49..f4700b999f23 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -391,14 +391,11 @@ static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, struct net_device *loopback_dev = dev_net(dev)->loopback_dev; - if (dev != loopback_dev) { - if (idev && idev->dev == dev) { - struct inet6_dev *loopback_idev = - in6_dev_get(loopback_dev); - if (loopback_idev) { - rt->rt6i_idev = loopback_idev; - in6_dev_put(idev); - } + if (idev && idev->dev != loopback_dev) { + struct inet6_dev *loopback_idev = in6_dev_get(loopback_dev); + if (loopback_idev) { + rt->rt6i_idev = loopback_idev; + in6_dev_put(idev); } } } diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index e7c1b052c2a3..7b06813bf8a1 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -2804,7 +2804,7 @@ static struct genl_family ip_vs_genl_family = { .hdrsize = 0, .name = IPVS_GENL_NAME, .version = IPVS_GENL_VERSION, - .maxattr = IPVS_CMD_MAX, + .maxattr = IPVS_CMD_ATTR_MAX, .netnsok = true, /* Make ipvsadm to work on netns */ }; diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index fef69cb21f6f..438fad9bca5f 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -519,13 +519,11 @@ static struct tag_ref *get_tag_ref(tag_t full_tag, DR_DEBUG("qtaguid: get_tag_ref(0x%llx)\n", full_tag); - spin_lock_bh(&uid_tag_data_tree_lock); tr_entry = lookup_tag_ref(full_tag, &utd_entry); BUG_ON(IS_ERR_OR_NULL(utd_entry)); if (!tr_entry) tr_entry = new_tag_ref(full_tag, utd_entry); - spin_unlock_bh(&uid_tag_data_tree_lock); if (utd_res) *utd_res = utd_entry; DR_DEBUG("qtaguid: get_tag_ref(0x%llx) utd=%p tr=%p\n", @@ -2028,6 +2026,7 @@ static int ctrl_cmd_delete(const char *input) /* Delete socket tags */ spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); node = rb_first(&sock_tag_tree); while (node) { st_entry = rb_entry(node, struct sock_tag, sock_node); @@ -2057,6 +2056,7 @@ static int ctrl_cmd_delete(const char *input) list_del(&st_entry->list); } } + spin_unlock_bh(&uid_tag_data_tree_lock); spin_unlock_bh(&sock_tag_list_lock); sock_tag_tree_erase(&st_to_free_tree); @@ -2266,10 +2266,12 @@ static int ctrl_cmd_tag(const char *input) full_tag = combine_atag_with_uid(acct_tag, uid_int); spin_lock_bh(&sock_tag_list_lock); + spin_lock_bh(&uid_tag_data_tree_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); tag_ref_entry = get_tag_ref(full_tag, &uid_tag_data_entry); if (IS_ERR(tag_ref_entry)) { res = PTR_ERR(tag_ref_entry); + spin_unlock_bh(&uid_tag_data_tree_lock); spin_unlock_bh(&sock_tag_list_lock); goto err_put; } @@ -2296,9 +2298,14 @@ static int ctrl_cmd_tag(const char *input) pr_err("qtaguid: ctrl_tag(%s): " "socket tag alloc failed\n", input); + BUG_ON(tag_ref_entry->num_sock_tags <= 0); + tag_ref_entry->num_sock_tags--; + free_tag_ref_from_utd_entry(tag_ref_entry, + uid_tag_data_entry); + spin_unlock_bh(&uid_tag_data_tree_lock); spin_unlock_bh(&sock_tag_list_lock); res = -ENOMEM; - goto err_tag_unref_put; + goto err_put; } /* * Hold the sk refcount here to make sure the sk pointer cannot @@ -2308,7 +2315,6 @@ static int ctrl_cmd_tag(const char *input) sock_tag_entry->sk = el_socket->sk; sock_tag_entry->pid = current->tgid; sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid_int); - spin_lock_bh(&uid_tag_data_tree_lock); pqd_entry = proc_qtu_data_tree_search( &proc_qtu_data_tree, current->tgid); /* @@ -2326,11 +2332,11 @@ static int ctrl_cmd_tag(const char *input) else list_add(&sock_tag_entry->list, &pqd_entry->sock_tag_list); - spin_unlock_bh(&uid_tag_data_tree_lock); sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); atomic64_inc(&qtu_events.sockets_tagged); } + spin_unlock_bh(&uid_tag_data_tree_lock); spin_unlock_bh(&sock_tag_list_lock); /* We keep the ref to the sk until it is untagged */ CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->sk_refcnt=%d\n", @@ -2339,10 +2345,6 @@ static int ctrl_cmd_tag(const char *input) sockfd_put(el_socket); return 0; -err_tag_unref_put: - BUG_ON(tag_ref_entry->num_sock_tags <= 0); - tag_ref_entry->num_sock_tags--; - free_tag_ref_from_utd_entry(tag_ref_entry, uid_tag_data_entry); err_put: CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->sk_refcnt=%d\n", input, atomic_read(&el_socket->sk->sk_refcnt) - 1); diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index ede54061c554..939821821fcb 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -158,10 +158,13 @@ struct sock *xt_socket_lookup_slow_v4(struct net *net, #endif if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) { - struct udphdr _hdr, *hp; + struct udphdr *hp; + struct tcphdr _hdr; hp = skb_header_pointer(skb, ip_hdrlen(skb), - sizeof(_hdr), &_hdr); + iph->protocol == IPPROTO_UDP ? + sizeof(*hp) : sizeof(_hdr), + &_hdr); if (hp == NULL) return NULL; @@ -360,9 +363,11 @@ struct sock *xt_socket_lookup_slow_v6(struct net *net, } if (tproto == IPPROTO_UDP || tproto == IPPROTO_TCP) { - struct udphdr _hdr, *hp; + struct udphdr *hp; + struct tcphdr _hdr; - hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr); + hp = skb_header_pointer(skb, thoff, tproto == IPPROTO_UDP ? + sizeof(*hp) : sizeof(_hdr), &_hdr); if (hp == NULL) return NULL; diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 2141d047301d..f7bf6e1cec7a 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -223,6 +223,9 @@ static int __netlink_deliver_tap_skb(struct sk_buff *skb, 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/nfc/hci/core.c b/net/nfc/hci/core.c index 2b0f0ac498d2..5a58f9f38095 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -209,6 +209,11 @@ void nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, } create_info = (struct hci_create_pipe_resp *)skb->data; + if (create_info->pipe >= NFC_HCI_MAX_PIPES) { + status = NFC_HCI_ANY_E_NOK; + goto exit; + } + /* Save the new created pipe and bind with local gate, * the description for skb->data[3] is destination gate id * but since we received this cmd from host controller, we @@ -232,6 +237,11 @@ void nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, } delete_info = (struct hci_delete_pipe_noti *)skb->data; + if (delete_info->pipe >= NFC_HCI_MAX_PIPES) { + status = NFC_HCI_ANY_E_NOK; + goto exit; + } + hdev->pipes[delete_info->pipe].gate = NFC_HCI_INVALID_GATE; hdev->pipes[delete_info->pipe].dest_host = NFC_HCI_INVALID_HOST; break; diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index f8d6a0ca9c03..49f719ad638b 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1652,10 +1652,6 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) mutex_lock(&fanout_mutex); - err = -EINVAL; - if (!po->running) - goto out; - err = -EALREADY; if (po->fanout) goto out; @@ -1694,7 +1690,7 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) match->flags = flags; INIT_LIST_HEAD(&match->list); spin_lock_init(&match->lock); - atomic_set(&match->sk_ref, 0); + refcount_set(&match->sk_ref, 0); fanout_init_data(match); match->prot_hook.type = po->prot_hook.type; match->prot_hook.dev = po->prot_hook.dev; @@ -1704,18 +1700,28 @@ static int fanout_add(struct sock *sk, u16 id, u16 type_flags) list_add(&match->list, &fanout_list); } err = -EINVAL; - if (match->type == type && + + spin_lock(&po->bind_lock); + if (po->running && + match->type == type && match->prot_hook.type == po->prot_hook.type && match->prot_hook.dev == po->prot_hook.dev) { err = -ENOSPC; - if (atomic_read(&match->sk_ref) < PACKET_FANOUT_MAX) { + if (refcount_read(&match->sk_ref) < PACKET_FANOUT_MAX) { __dev_remove_pack(&po->prot_hook); po->fanout = match; - atomic_inc(&match->sk_ref); + refcount_set(&match->sk_ref, refcount_read(&match->sk_ref) + 1); __fanout_link(sk, po); err = 0; } } + spin_unlock(&po->bind_lock); + + if (err && !refcount_read(&match->sk_ref)) { + list_del(&match->list); + kfree(match); + } + out: if (err && rollover) { kfree(rollover); @@ -1740,7 +1746,7 @@ static struct packet_fanout *fanout_release(struct sock *sk) if (f) { po->fanout = NULL; - if (atomic_dec_and_test(&f->sk_ref)) + if (refcount_dec_and_test(&f->sk_ref)) list_del(&f->list); else f = NULL; @@ -3622,14 +3628,19 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv if (optlen != sizeof(val)) return -EINVAL; - if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) - return -EBUSY; if (copy_from_user(&val, optval, sizeof(val))) return -EFAULT; if (val > INT_MAX) return -EINVAL; - po->tp_reserve = val; - return 0; + lock_sock(sk); + if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) { + ret = -EBUSY; + } else { + po->tp_reserve = val; + ret = 0; + } + release_sock(sk); + return ret; } case PACKET_LOSS: { diff --git a/net/packet/internal.h b/net/packet/internal.h index 9ee46314b7d7..94d1d405a116 100644 --- a/net/packet/internal.h +++ b/net/packet/internal.h @@ -1,6 +1,8 @@ #ifndef __PACKET_INTERNAL_H__ #define __PACKET_INTERNAL_H__ +#include + struct packet_mclist { struct packet_mclist *next; int ifindex; @@ -86,7 +88,7 @@ struct packet_fanout { struct list_head list; struct sock *arr[PACKET_FANOUT_MAX]; spinlock_t lock; - atomic_t sk_ref; + refcount_t sk_ref; struct packet_type prot_hook ____cacheline_aligned_in_smp; }; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 3ebf3b652d60..fe5012e8ffc7 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4423,6 +4423,10 @@ int sctp_do_peeloff(struct sock *sk, sctp_assoc_t id, struct socket **sockp) struct socket *sock; int err = 0; + /* Do not peel off from one netns to another one. */ + if (!net_eq(current->nsproxy->net_ns, sock_net(sk))) + return -EINVAL; + if (!asoc) return -EINVAL; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 0f45e1a3a7d1..7e377a493cc7 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1214,7 +1214,7 @@ static inline int policy_to_flow_dir(int dir) } static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir, - const struct flowi *fl) + const struct flowi *fl, u16 family) { struct xfrm_policy *pol; struct net *net = sock_net(sk); @@ -1223,10 +1223,15 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir, read_lock_bh(&net->xfrm.xfrm_policy_lock); pol = rcu_dereference(sk->sk_policy[dir]); if (pol != NULL) { - bool match = xfrm_selector_match(&pol->selector, fl, - sk->sk_family); + bool match; int err = 0; + if (pol->family != family) { + pol = NULL; + goto out; + } + + match = xfrm_selector_match(&pol->selector, fl, family); if (match) { if ((sk->sk_mark & pol->mark.m) != pol->mark.v) { pol = NULL; @@ -2171,7 +2176,7 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig, sk = sk_const_to_full_sk(sk); if (sk && sk->sk_policy[XFRM_POLICY_OUT]) { num_pols = 1; - pols[0] = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl); + pols[0] = xfrm_sk_policy_lookup(sk, XFRM_POLICY_OUT, fl, family); err = xfrm_expand_policies(fl, family, pols, &num_pols, &num_xfrms); if (err < 0) @@ -2450,7 +2455,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, pol = NULL; sk = sk_to_full_sk(sk); if (sk && sk->sk_policy[dir]) { - pol = xfrm_sk_policy_lookup(sk, dir, &fl); + pol = xfrm_sk_policy_lookup(sk, dir, &fl, family); if (IS_ERR(pol)) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR); return 0; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 7a5a64e70b4d..1d1ed6484dcf 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1376,11 +1376,14 @@ static void copy_templates(struct xfrm_policy *xp, struct xfrm_user_tmpl *ut, static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) { + u16 prev_family; int i; if (nr > XFRM_MAX_DEPTH) return -EINVAL; + prev_family = family; + for (i = 0; i < nr; i++) { /* We never validated the ut->family value, so many * applications simply leave it at zero. The check was @@ -1392,6 +1395,12 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family) if (!ut[i].family) ut[i].family = family; + if ((ut[i].mode == XFRM_MODE_TRANSPORT) && + (ut[i].family != prev_family)) + return -EINVAL; + + prev_family = ut[i].family; + switch (ut[i].family) { case AF_INET: break; diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 31898856682e..d85dcd9e7d38 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -141,23 +141,22 @@ static int valid_ecryptfs_desc(const char *ecryptfs_desc) */ static int valid_master_desc(const char *new_desc, const char *orig_desc) { - if (!memcmp(new_desc, KEY_TRUSTED_PREFIX, KEY_TRUSTED_PREFIX_LEN)) { - if (strlen(new_desc) == KEY_TRUSTED_PREFIX_LEN) - goto out; - if (orig_desc) - if (memcmp(new_desc, orig_desc, KEY_TRUSTED_PREFIX_LEN)) - goto out; - } else if (!memcmp(new_desc, KEY_USER_PREFIX, KEY_USER_PREFIX_LEN)) { - if (strlen(new_desc) == KEY_USER_PREFIX_LEN) - goto out; - if (orig_desc) - if (memcmp(new_desc, orig_desc, KEY_USER_PREFIX_LEN)) - goto out; - } else - goto out; + int prefix_len; + + if (!strncmp(new_desc, KEY_TRUSTED_PREFIX, KEY_TRUSTED_PREFIX_LEN)) + prefix_len = KEY_TRUSTED_PREFIX_LEN; + else if (!strncmp(new_desc, KEY_USER_PREFIX, KEY_USER_PREFIX_LEN)) + prefix_len = KEY_USER_PREFIX_LEN; + else + return -EINVAL; + + if (!new_desc[prefix_len]) + return -EINVAL; + + if (orig_desc && strncmp(new_desc, orig_desc, prefix_len)) + return -EINVAL; + return 0; -out: - return -EINVAL; } /* diff --git a/security/keys/request_key.c b/security/keys/request_key.c index c7a117c9a8f3..679492f25d0b 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -250,11 +250,12 @@ static int construct_key(struct key *key, const void *callout_info, * The keyring selected is returned with an extra reference upon it which the * caller must release. */ -static void construct_get_dest_keyring(struct key **_dest_keyring) +static int construct_get_dest_keyring(struct key **_dest_keyring) { struct request_key_auth *rka; const struct cred *cred = current_cred(); struct key *dest_keyring = *_dest_keyring, *authkey; + int ret; kenter("%p", dest_keyring); @@ -263,6 +264,8 @@ static void construct_get_dest_keyring(struct key **_dest_keyring) /* the caller supplied one */ key_get(dest_keyring); } else { + bool do_perm_check = true; + /* use a default keyring; falling through the cases until we * find one that we actually have */ switch (cred->jit_keyring) { @@ -277,8 +280,10 @@ static void construct_get_dest_keyring(struct key **_dest_keyring) dest_keyring = key_get(rka->dest_keyring); up_read(&authkey->sem); - if (dest_keyring) + if (dest_keyring) { + do_perm_check = false; break; + } } case KEY_REQKEY_DEFL_THREAD_KEYRING: @@ -313,11 +318,29 @@ static void construct_get_dest_keyring(struct key **_dest_keyring) default: BUG(); } + + /* + * Require Write permission on the keyring. This is essential + * because the default keyring may be the session keyring, and + * joining a keyring only requires Search permission. + * + * However, this check is skipped for the "requestor keyring" so + * that /sbin/request-key can itself use request_key() to add + * keys to the original requestor's destination keyring. + */ + if (dest_keyring && do_perm_check) { + ret = key_permission(make_key_ref(dest_keyring, 1), + KEY_NEED_WRITE); + if (ret) { + key_put(dest_keyring); + return ret; + } + } } *_dest_keyring = dest_keyring; kleave(" [dk %d]", key_serial(dest_keyring)); - return; + return 0; } /* @@ -442,12 +465,16 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx, if (ctx->index_key.type == &key_type_keyring) return ERR_PTR(-EPERM); - - user = key_user_lookup(current_fsuid()); - if (!user) - return ERR_PTR(-ENOMEM); - construct_get_dest_keyring(&dest_keyring); + ret = construct_get_dest_keyring(&dest_keyring); + if (ret) + goto error; + + user = key_user_lookup(current_fsuid()); + if (!user) { + ret = -ENOMEM; + goto error_put_dest_keyring; + } ret = construct_alloc_key(ctx, dest_keyring, flags, user, &key); key_user_put(user); @@ -462,7 +489,7 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx, } else if (ret == -EINPROGRESS) { ret = 0; } else { - goto couldnt_alloc_key; + goto error_put_dest_keyring; } key_put(dest_keyring); @@ -472,8 +499,9 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx, construction_failed: key_negate_and_link(key, key_negative_timeout, NULL, NULL); key_put(key); -couldnt_alloc_key: +error_put_dest_keyring: key_put(dest_keyring); +error: kleave(" = %d", ret); return ERR_PTR(ret); } diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index ebb5eb3c318c..9d5ded15f014 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1440,7 +1440,7 @@ static int security_context_to_sid_core(const char *scontext, u32 scontext_len, scontext_len, &context, def_sid); if (rc == -EINVAL && force) { context.str = str; - context.len = scontext_len; + context.len = strlen(str) + 1; str = NULL; } else if (rc) goto out_unlock; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index b450a27588c8..0a72a49a902c 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -115,6 +115,7 @@ static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream) return -ENOMEM; runtime->substream = substream; spin_lock_init(&runtime->lock); + mutex_init(&runtime->realloc_mutex); init_waitqueue_head(&runtime->sleep); INIT_WORK(&runtime->event_work, snd_rawmidi_input_event_work); runtime->event = NULL; @@ -627,8 +628,10 @@ int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream, struct snd_rawmidi_params * params) { char *newbuf; + char *oldbuf; struct snd_rawmidi_runtime *runtime = substream->runtime; - + unsigned long flags; + if (substream->append && substream->use_count > 1) return -EBUSY; snd_rawmidi_drain_output(substream); @@ -639,13 +642,22 @@ int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream, return -EINVAL; } if (params->buffer_size != runtime->buffer_size) { - newbuf = krealloc(runtime->buffer, params->buffer_size, + mutex_lock(&runtime->realloc_mutex); + newbuf = __krealloc(runtime->buffer, params->buffer_size, GFP_KERNEL); - if (!newbuf) + if (!newbuf) { + mutex_unlock(&runtime->realloc_mutex); return -ENOMEM; + } + spin_lock_irqsave(&runtime->lock, flags); + oldbuf = runtime->buffer; runtime->buffer = newbuf; runtime->buffer_size = params->buffer_size; runtime->avail = runtime->buffer_size; + spin_unlock_irqrestore(&runtime->lock, flags); + if (oldbuf != newbuf) + kfree(oldbuf); + mutex_unlock(&runtime->realloc_mutex); } runtime->avail_min = params->avail_min; substream->active_sensing = !params->no_active_sensing; @@ -657,7 +669,9 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, struct snd_rawmidi_params * params) { char *newbuf; + char *oldbuf; struct snd_rawmidi_runtime *runtime = substream->runtime; + unsigned long flags; snd_rawmidi_drain_input(substream); if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) { @@ -667,12 +681,21 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, return -EINVAL; } if (params->buffer_size != runtime->buffer_size) { - newbuf = krealloc(runtime->buffer, params->buffer_size, + mutex_lock(&runtime->realloc_mutex); + newbuf = __krealloc(runtime->buffer, params->buffer_size, GFP_KERNEL); - if (!newbuf) + if (!newbuf) { + mutex_unlock(&runtime->realloc_mutex); return -ENOMEM; + } + spin_lock_irqsave(&runtime->lock, flags); + oldbuf = runtime->buffer; runtime->buffer = newbuf; runtime->buffer_size = params->buffer_size; + spin_unlock_irqrestore(&runtime->lock, flags); + if (oldbuf != newbuf) + kfree(oldbuf); + mutex_unlock(&runtime->realloc_mutex); } runtime->avail_min = params->avail_min; return 0; @@ -944,6 +967,8 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, struct snd_rawmidi_runtime *runtime = substream->runtime; unsigned long appl_ptr; + if (userbuf) + mutex_lock(&runtime->realloc_mutex); spin_lock_irqsave(&runtime->lock, flags); while (count > 0 && runtime->avail) { count1 = runtime->buffer_size - runtime->appl_ptr; @@ -964,6 +989,7 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, spin_unlock_irqrestore(&runtime->lock, flags); if (copy_to_user(userbuf + result, runtime->buffer + appl_ptr, count1)) { + mutex_unlock(&runtime->realloc_mutex); return result > 0 ? result : -EFAULT; } spin_lock_irqsave(&runtime->lock, flags); @@ -972,6 +998,8 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream, count -= count1; } spin_unlock_irqrestore(&runtime->lock, flags); + if (userbuf) + mutex_unlock(&runtime->realloc_mutex); return result; } @@ -1236,10 +1264,14 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, return -EINVAL; result = 0; + if (userbuf) + mutex_lock(&runtime->realloc_mutex); spin_lock_irqsave(&runtime->lock, flags); if (substream->append) { if ((long)runtime->avail < count) { spin_unlock_irqrestore(&runtime->lock, flags); + if (userbuf) + mutex_unlock(&runtime->realloc_mutex); return -EAGAIN; } } @@ -1275,6 +1307,8 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, __end: count1 = runtime->avail < runtime->buffer_size; spin_unlock_irqrestore(&runtime->lock, flags); + if (userbuf) + mutex_unlock(&runtime->realloc_mutex); if (count1) snd_rawmidi_output_trigger(substream, 1); return result; diff --git a/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c b/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c index 58df020811f1..8e69275ac7c1 100644 --- a/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c +++ b/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c @@ -256,8 +256,8 @@ static int msm_int_enable_sdw_cdc_clk(struct msm_sdw_priv *msm_sdw, msm_sdw->int_mclk1_enabled = false; } } - mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex); rtn: + mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex); return ret; } EXPORT_SYMBOL(msm_int_enable_sdw_cdc_clk); diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c index 6c1fa9dac236..ba6ab31d294e 100644 --- a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c +++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.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. * Copyright (C) 2018 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify @@ -2802,7 +2802,7 @@ static void wcd_imped_config(struct snd_soc_codec *codec, 0x20, 0x00); snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_NCP_VCTRL, - 0x07, 0x04); + 0x07, 0x07); } break; } @@ -2871,7 +2871,11 @@ static int msm_anlg_cdc_hphl_dac_event(struct snd_soc_dapm_widget *w, MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x02, 0x00); break; case SND_SOC_DAPM_POST_PMD: - wcd_imped_config(codec, impedl, false); + if (!ret) + wcd_imped_config(codec, impedl, false); + else + dev_dbg(codec->dev, "Failed to get mbhc impedance %d\n", + ret); snd_soc_update_bits(codec, MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x02, 0x00); snd_soc_update_bits(codec, diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c index 29c218013a07..078e60fdca87 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c +++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -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 @@ -31,6 +31,7 @@ #define WCD_PROCFS_ENTRY_MAX_LEN 16 #define WCD_934X_RAMDUMP_START_ADDR 0x20100000 #define WCD_934X_RAMDUMP_SIZE ((1024 * 1024) - 128) +#define WCD_DSP_CNTL_MAX_COUNT 2 #define WCD_CNTL_MUTEX_LOCK(codec, lock) \ { \ @@ -909,11 +910,13 @@ static ssize_t wcd_miscdev_write(struct file *filep, const char __user *ubuf, { struct wcd_dsp_cntl *cntl = container_of(filep->private_data, struct wcd_dsp_cntl, miscdev); - char val[count]; + char val[WCD_DSP_CNTL_MAX_COUNT + 1]; bool vote; int ret = 0; - if (count == 0 || count > 2) { + memset(val, 0, WCD_DSP_CNTL_MAX_COUNT + 1); + + if (count == 0 || count > WCD_DSP_CNTL_MAX_COUNT) { pr_err("%s: Invalid count = %zd\n", __func__, count); ret = -EINVAL; goto done; diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c index 9218786f913f..337c25f869d6 100644 --- a/sound/soc/codecs/wcd_cpe_core.c +++ b/sound/soc/codecs/wcd_cpe_core.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 @@ -337,6 +337,14 @@ static int wcd_cpe_load_each_segment(struct wcd_cpe_core *core, goto done; } + if (phdr->p_filesz != split_fw->size) { + dev_err(core->dev, + "%s: %s size mismatch, phdr_size: 0x%x fw_size: 0x%zx", + __func__, split_fname, phdr->p_filesz, split_fw->size); + ret = -EINVAL; + goto done; + } + segment->cpe_addr = phdr->p_paddr; segment->size = phdr->p_filesz; segment->data = (u8 *) split_fw->data; @@ -1945,6 +1953,7 @@ struct wcd_cpe_core *wcd_cpe_init(const char *img_fname, init_completion(&core->online_compl); init_waitqueue_head(&core->ssr_entry.offline_poll_wait); mutex_init(&core->ssr_lock); + mutex_init(&core->session_lock); core->cpe_users = 0; core->cpe_clk_ref = 0; @@ -3399,6 +3408,7 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( * If this is the first session to be allocated, * only then register the afe service. */ + WCD_CPE_GRAB_LOCK(&core->session_lock, "session_lock"); if (!wcd_cpe_lsm_session_active()) afe_register_service = true; @@ -3413,6 +3423,7 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( dev_err(core->dev, "%s: max allowed sessions already allocated\n", __func__); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return NULL; } @@ -3421,6 +3432,7 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( dev_err(core->dev, "%s: Failed to enable cpe, err = %d\n", __func__, ret); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return NULL; } @@ -3467,6 +3479,8 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( init_completion(&session->cmd_comp); lsm_sessions[session_id] = session; + + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return session; err_afe_mode_cmd: @@ -3481,6 +3495,7 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( err_session_alloc: wcd_cpe_vote(core, false); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return NULL; } @@ -3632,9 +3647,11 @@ static int wcd_cpe_dealloc_lsm_session(void *core_handle, struct wcd_cpe_core *core = core_handle; int ret = 0; + WCD_CPE_GRAB_LOCK(&core->session_lock, "session_lock"); if (!session) { dev_err(core->dev, "%s: Invalid lsm session\n", __func__); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return -EINVAL; } @@ -3645,6 +3662,7 @@ static int wcd_cpe_dealloc_lsm_session(void *core_handle, "%s: Wrong session id %d max allowed = %d\n", __func__, session->id, WCD_CPE_LSM_MAX_SESSIONS); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return -EINVAL; } @@ -3665,6 +3683,7 @@ static int wcd_cpe_dealloc_lsm_session(void *core_handle, "%s: Failed to un-vote cpe, err = %d\n", __func__, ret); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return ret; } diff --git a/sound/soc/codecs/wcd_cpe_core.h b/sound/soc/codecs/wcd_cpe_core.h index b09b03d2f81d..1ac393471197 100644 --- a/sound/soc/codecs/wcd_cpe_core.h +++ b/sound/soc/codecs/wcd_cpe_core.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-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 @@ -172,6 +172,9 @@ struct wcd_cpe_core { /* mutex to protect cpe ssr status variables */ struct mutex ssr_lock; + /* mutex to protect cpe session status variables */ + struct mutex session_lock; + /* Store the calibration data needed for cpe */ struct cal_type_data *cal_data[WCD_CPE_LSM_CAL_MAX]; diff --git a/sound/soc/codecs/wcd_cpe_services.c b/sound/soc/codecs/wcd_cpe_services.c index 582fddfa1096..58f02f1f35f5 100644 --- a/sound/soc/codecs/wcd_cpe_services.c +++ b/sound/soc/codecs/wcd_cpe_services.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-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 @@ -615,8 +615,10 @@ static enum cpe_svc_result cpe_deregister_generic(struct cpe_info *t_info, return CPE_SVC_INVALID_HANDLE; } + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); list_del(&(n->list)); kfree(reg_handle); + CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); return CPE_SVC_SUCCESS; } diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c index 44fc5558ed9d..d6b988437334 100644 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.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 @@ -3712,6 +3712,7 @@ static int msm_compr_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, struct msm_compr_audio *prtd; int ret = 0; struct msm_adsp_event_data *event_data = NULL; + uint64_t actual_payload_len = 0; if (fe_id >= MSM_FRONTEND_DAI_MAX) { pr_err("%s Received invalid fe_id %lu\n", @@ -3749,8 +3750,17 @@ static int msm_compr_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, goto done; } - if ((sizeof(struct msm_adsp_event_data) + event_data->payload_len) >= - sizeof(ucontrol->value.bytes.data)) { + actual_payload_len = sizeof(struct msm_adsp_event_data) + + event_data->payload_len; + if (actual_payload_len >= U32_MAX) { + pr_err("%s payload length 0x%X exceeds limit", + __func__, event_data->payload_len); + ret = -EINVAL; + goto done; + } + + if (event_data->payload_len > sizeof(ucontrol->value.bytes.data) + - sizeof(struct msm_adsp_event_data)) { pr_err("%s param length=%d exceeds limit", __func__, event_data->payload_len); ret = -EINVAL; diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c index 292b3d04f7d5..472ea1db2859 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.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 @@ -3571,13 +3571,13 @@ static int msm_dai_q6_dai_mi2s_probe(struct snd_soc_dai *dai) ctrl = NULL; if (mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.channel_mode) { if (dai->id == MSM_PRIM_MI2S) - ctrl = &mi2s_config_controls[4]; - if (dai->id == MSM_SEC_MI2S) ctrl = &mi2s_config_controls[5]; - if (dai->id == MSM_TERT_MI2S) + if (dai->id == MSM_SEC_MI2S) ctrl = &mi2s_config_controls[6]; - if (dai->id == MSM_QUAT_MI2S) + if (dai->id == MSM_TERT_MI2S) ctrl = &mi2s_config_controls[7]; + if (dai->id == MSM_QUAT_MI2S) + ctrl = &mi2s_config_controls[8]; if (dai->id == MSM_QUIN_MI2S) ctrl = &mi2s_config_controls[9]; if (dai->id == MSM_SENARY_MI2S) @@ -3586,9 +3586,6 @@ static int msm_dai_q6_dai_mi2s_probe(struct snd_soc_dai *dai) ctrl = &mi2s_config_controls[11]; } - if (dai->id == MSM_QUAT_MI2S) - ctrl = &mi2s_config_controls[8]; - if (ctrl) { rc = snd_ctl_add(dai->component->card->snd_card, snd_ctl_new1(ctrl, @@ -4164,18 +4161,6 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = { .probe = msm_dai_q6_dai_mi2s_probe, .remove = msm_dai_q6_dai_mi2s_remove, }, - { - .playback = { - .stream_name = "Secondary MI2S Playback SD1", - .aif_name = "SEC_MI2S_RX_SD1", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rate_min = 8000, - .rate_max = 48000, - }, - .id = MSM_SEC_MI2S_SD1, - }, { .playback = { .stream_name = "Quinary MI2S Playback", @@ -4201,6 +4186,18 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = { .probe = msm_dai_q6_dai_mi2s_probe, .remove = msm_dai_q6_dai_mi2s_remove, }, + { + .playback = { + .stream_name = "Secondary MI2S Playback SD1", + .aif_name = "SEC_MI2S_RX_SD1", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .rate_max = 48000, + }, + .id = MSM_SEC_MI2S_SD1, + }, { .capture = { .stream_name = "Senary_mi2s Capture", diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c index 7e022619c097..35c4583b0ff4 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.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 @@ -52,6 +52,7 @@ static struct audio_locks the_locks; static const DECLARE_TLV_DB_LINEAR(msm_pcm_vol_gain, 0, PCM_MASTER_VOL_MAX_STEPS); + struct snd_msm { struct snd_card *card; struct snd_pcm *pcm; @@ -1084,6 +1085,7 @@ static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, struct msm_audio *prtd; int ret = 0; struct msm_adsp_event_data *event_data = NULL; + uint64_t actual_payload_len = 0; if (!pdata) { pr_err("%s pdata is NULL\n", __func__); @@ -1120,8 +1122,17 @@ static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, goto done; } - if ((sizeof(struct msm_adsp_event_data) + event_data->payload_len) >= - sizeof(ucontrol->value.bytes.data)) { + actual_payload_len = sizeof(struct msm_adsp_event_data) + + event_data->payload_len; + if (actual_payload_len >= U32_MAX) { + pr_err("%s payload length 0x%X exceeds limit", + __func__, event_data->payload_len); + ret = -EINVAL; + goto done; + } + + if (event_data->payload_len > sizeof(ucontrol->value.bytes.data) + - sizeof(struct msm_adsp_event_data)) { pr_err("%s param length=%d exceeds limit", __func__, event_data->payload_len); ret = -EINVAL; diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c index 65f5167d9dee..ecc0bc05cc83 100644 --- a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c +++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.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 @@ -962,8 +962,9 @@ int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, event_data = (struct msm_adsp_event_data *)payload; kctl->info(kctl, &kctl_info); - if (sizeof(struct msm_adsp_event_data) - + event_data->payload_len > kctl_info.count) { + + if (event_data->payload_len > + kctl_info.count - sizeof(struct msm_adsp_event_data)) { pr_err("%s: payload length exceeds limit of %u bytes.\n", __func__, kctl_info.count); ret = -EINVAL; diff --git a/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c index 18cac3424054..3b53614aba31 100644 --- a/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c +++ b/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.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 @@ -645,6 +645,7 @@ static int msm_transcode_stream_cmd_put(struct snd_kcontrol *kcontrol, struct msm_transcode_loopback *prtd; int ret = 0; struct msm_adsp_event_data *event_data = NULL; + uint64_t actual_payload_len = 0; if (fe_id >= MSM_FRONTEND_DAI_MAX) { pr_err("%s Received invalid fe_id %lu\n", @@ -682,8 +683,17 @@ static int msm_transcode_stream_cmd_put(struct snd_kcontrol *kcontrol, goto done; } - if ((sizeof(struct msm_adsp_event_data) + event_data->payload_len) >= - sizeof(ucontrol->value.bytes.data)) { + actual_payload_len = sizeof(struct msm_adsp_event_data) + + event_data->payload_len; + if (actual_payload_len >= U32_MAX) { + pr_err("%s payload length 0x%X exceeds limit", + __func__, event_data->payload_len); + ret = -EINVAL; + goto done; + } + + if (event_data->payload_len > sizeof(ucontrol->value.bytes.data) + - sizeof(struct msm_adsp_event_data)) { pr_err("%s param length=%d exceeds limit", __func__, event_data->payload_len); ret = -EINVAL; diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c index a96d6a973ba5..a99400ba9bf7 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/sound/soc/msm/qdsp6v2/q6asm.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. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -46,6 +46,7 @@ #define FALSE 0x00 #define SESSION_MAX 9 #define ASM_MAX_CHANNELS 8 + enum { ASM_TOPOLOGY_CAL = 0, ASM_CUSTOM_TOP_CAL, @@ -285,6 +286,11 @@ static ssize_t audio_output_latency_dbgfs_read(struct file *file, pr_err("%s: out_buffer is null\n", __func__); return 0; } + if (count < OUT_BUFFER_SIZE) { + pr_err("%s: read size %d exceeds buf size %zd\n", __func__, + OUT_BUFFER_SIZE, count); + return 0; + } snprintf(out_buffer, OUT_BUFFER_SIZE, "%ld,%ld,%ld,%ld,%ld,%ld,",\ out_cold_tv.tv_sec, out_cold_tv.tv_usec, out_warm_tv.tv_sec,\ out_warm_tv.tv_usec, out_cont_tv.tv_sec, out_cont_tv.tv_usec); @@ -338,6 +344,11 @@ static ssize_t audio_input_latency_dbgfs_read(struct file *file, pr_err("%s: in_buffer is null\n", __func__); return 0; } + if (count < IN_BUFFER_SIZE) { + pr_err("%s: read size %d exceeds buf size %zd\n", __func__, + IN_BUFFER_SIZE, count); + return 0; + } snprintf(in_buffer, IN_BUFFER_SIZE, "%ld,%ld,",\ in_cont_tv.tv_sec, in_cont_tv.tv_usec); return simple_read_from_buffer(buf, IN_BUFFER_SIZE, ppos, @@ -577,11 +588,12 @@ static bool q6asm_is_valid_audio_client(struct audio_client *ac) static void q6asm_session_free(struct audio_client *ac) { int session_id; + unsigned long flags; pr_debug("%s: sessionid[%d]\n", __func__, ac->session); session_id = ac->session; rtac_remove_popp_from_adm_devices(ac->session); - spin_lock_bh(&(session[session_id].session_lock)); + spin_lock_irqsave(&(session[session_id].session_lock), flags); session[ac->session].ac = NULL; ac->session = 0; ac->perf_mode = LEGACY_PCM_MODE; @@ -590,7 +602,8 @@ static void q6asm_session_free(struct audio_client *ac) ac->priv = NULL; kfree(ac); ac = NULL; - spin_unlock_bh(&(session[session_id].session_lock)); + spin_unlock_irqrestore(&(session[session_id].session_lock), flags); + return; } @@ -953,7 +966,7 @@ int q6asm_unmap_rtac_block(uint32_t *mem_map_handle) __func__, result2); result = result2; } else { - mem_map_handle = 0; + *mem_map_handle = 0; } result2 = q6asm_mmap_apr_dereg(); @@ -1156,7 +1169,9 @@ int q6asm_send_stream_cmd(struct audio_client *ac, { char *asm_params = NULL; struct apr_hdr hdr; - int sz, rc; + int rc; + uint32_t sz = 0; + uint64_t actual_sz = 0; if (!data || !ac) { pr_err("%s: %s is NULL\n", __func__, @@ -1173,7 +1188,15 @@ int q6asm_send_stream_cmd(struct audio_client *ac, goto done; } - sz = sizeof(struct apr_hdr) + data->payload_len; + actual_sz = sizeof(struct apr_hdr) + data->payload_len; + if (actual_sz > U32_MAX) { + pr_err("%s: payload size 0x%X exceeds limit\n", + __func__, data->payload_len); + rc = -EINVAL; + goto done; + } + + sz = (uint32_t)actual_sz; asm_params = kzalloc(sz, GFP_KERNEL); if (!asm_params) { rc = -ENOMEM; @@ -1534,6 +1557,7 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) uint32_t i = IN; uint32_t *payload; unsigned long dsp_flags; + unsigned long flags; struct asm_buffer_node *buf_node = NULL; struct list_head *ptr, *next; union asm_token_struct asm_token; @@ -1588,7 +1612,7 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) session_id = asm_token._token.session_id; if ((session_id > 0 && session_id <= ASM_ACTIVE_STREAMS_ALLOWED)) - spin_lock(&(session[session_id].session_lock)); + spin_lock_irqsave(&(session[session_id].session_lock), flags); ac = q6asm_get_audio_client(session_id); dir = q6asm_get_flag_from_token(&asm_token, ASM_DIRECTION_OFFSET); @@ -1598,7 +1622,8 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) __func__, session_id); if ((session_id > 0 && session_id <= ASM_ACTIVE_STREAMS_ALLOWED)) - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; } @@ -1651,7 +1676,8 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) } if ((session_id > 0 && session_id <= ASM_ACTIVE_STREAMS_ALLOWED)) - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; } @@ -1687,7 +1713,8 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) ac->cb(data->opcode, data->token, data->payload, ac->priv); if ((session_id > 0 && session_id <= ASM_ACTIVE_STREAMS_ALLOWED)) - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; } @@ -1756,6 +1783,7 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) uint8_t buf_index; struct msm_adsp_event_data *pp_event_package = NULL; uint32_t payload_size = 0; + unsigned long flags; int session_id; if (ac == NULL) { @@ -1774,11 +1802,13 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) return -EINVAL; } - spin_lock(&(session[session_id].session_lock)); + spin_lock_irqsave(&(session[session_id].session_lock), flags); + if (!q6asm_is_valid_audio_client(ac)) { pr_err("%s: audio client pointer is invalid, ac = %pK\n", __func__, ac); - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return -EINVAL; } @@ -1791,9 +1821,6 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) } if (data->opcode == RESET_EVENTS) { - spin_unlock(&(session[session_id].session_lock)); - mutex_lock(&ac->cmd_lock); - spin_lock(&(session[session_id].session_lock)); atomic_set(&ac->reset, 1); if (ac->apr == NULL) { ac->apr = ac->apr2; @@ -1814,8 +1841,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) wake_up(&ac->time_wait); wake_up(&ac->cmd_wait); wake_up(&ac->mem_wait); - spin_unlock(&(session[session_id].session_lock)); - mutex_unlock(&ac->cmd_lock); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; } @@ -1829,7 +1856,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) (data->opcode != ASM_SESSION_EVENT_RX_UNDERFLOW)) { if (payload == NULL) { pr_err("%s: payload is null\n", __func__); - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return -EINVAL; } dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x] opcode 0x%x\n", @@ -1855,7 +1883,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) ret = q6asm_is_valid_session(data, priv); if (ret != 0) { pr_err("%s: session invalid %d\n", __func__, ret); - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return ret; } case ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2: @@ -1895,8 +1924,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) payload[1]); wake_up(&ac->cmd_wait); } - spin_unlock( - &(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); return 0; } if ((is_adsp_reg_event(payload[0]) >= 0) || @@ -1927,8 +1957,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) atomic_set(&ac->mem_state, payload[1]); wake_up(&ac->mem_wait); } - spin_unlock( - &(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); return 0; } if (atomic_read(&ac->mem_state) && wakeup_flag) { @@ -1977,7 +2008,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) break; } - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; } @@ -1991,8 +2023,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) if (port->buf == NULL) { pr_err("%s: Unexpected Write Done\n", __func__); - spin_unlock( - &(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); return -EINVAL; } spin_lock_irqsave(&port->dsp_lock, dsp_flags); @@ -2007,8 +2040,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) __func__, payload[0], payload[1]); spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); - spin_unlock( - &(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); return -EINVAL; } port->buf[buf_index].used = 1; @@ -2079,8 +2113,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) if (ac->io_mode & SYNC_IO_MODE) { if (port->buf == NULL) { pr_err("%s: Unexpected Write Done\n", __func__); - spin_unlock( - &(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); return -EINVAL; } spin_lock_irqsave(&port->dsp_lock, dsp_flags); @@ -2156,7 +2191,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) __func__, payload[0], payload[1]); i = is_adsp_raise_event(data->opcode); if (i < 0) { - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; } @@ -2168,7 +2204,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) + sizeof(struct msm_adsp_event_data), GFP_ATOMIC); if (!pp_event_package) { - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return -ENOMEM; } @@ -2179,7 +2216,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) ac->cb(data->opcode, data->token, (void *)pp_event_package, ac->priv); kfree(pp_event_package); - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; case ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2: pr_debug("%s: ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2 sesion %d status 0x%x msw %u lsw %u\n", @@ -2205,7 +2243,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) if (ac->cb) ac->cb(data->opcode, data->token, data->payload, ac->priv); - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; } @@ -2381,11 +2420,16 @@ int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac) static void __q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, uint32_t pkt_size, uint32_t cmd_flg, uint32_t stream_id) { + unsigned long flags; + dev_vdbg(ac->dev, "%s: pkt_size=%d cmd_flg=%d session=%d stream_id=%d\n", __func__, pkt_size, cmd_flg, ac->session, stream_id); mutex_lock(&ac->cmd_lock); + spin_lock_irqsave(&(session[ac->session].session_lock), flags); if (ac->apr == NULL) { pr_err("%s: AC APR handle NULL", __func__); + spin_unlock_irqrestore( + &(session[ac->session].session_lock), flags); mutex_unlock(&ac->cmd_lock); return; } @@ -2408,6 +2452,8 @@ static void __q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, WAIT_CMD); hdr->pkt_size = pkt_size; + spin_unlock_irqrestore( + &(session[ac->session].session_lock), flags); mutex_unlock(&ac->cmd_lock); return; } diff --git a/sound/soc/msm/qdsp6v2/rtac.c b/sound/soc/msm/qdsp6v2/rtac.c index 77c6dfbbe8c1..045af70dfad8 100644 --- a/sound/soc/msm/qdsp6v2/rtac.c +++ b/sound/soc/msm/qdsp6v2/rtac.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 @@ -127,6 +127,11 @@ struct mutex rtac_voice_mutex; struct mutex rtac_voice_apr_mutex; struct mutex rtac_afe_apr_mutex; +static struct mutex rtac_asm_cal_mutex; +static struct mutex rtac_adm_cal_mutex; +static struct mutex rtac_afe_cal_mutex; +static struct mutex rtac_voice_cal_mutex; + int rtac_clear_mapping(uint32_t cal_type) { int result = 0; @@ -1713,42 +1718,62 @@ static long rtac_ioctl_shared(struct file *f, } case AUDIO_GET_RTAC_ADM_CAL: + mutex_lock(&rtac_adm_cal_mutex); result = send_adm_apr((void *)arg, ADM_CMD_GET_PP_PARAMS_V5); + mutex_unlock(&rtac_adm_cal_mutex); break; case AUDIO_SET_RTAC_ADM_CAL: + mutex_lock(&rtac_adm_cal_mutex); result = send_adm_apr((void *)arg, ADM_CMD_SET_PP_PARAMS_V5); + mutex_unlock(&rtac_adm_cal_mutex); break; case AUDIO_GET_RTAC_ASM_CAL: + mutex_lock(&rtac_asm_cal_mutex); result = send_rtac_asm_apr((void *)arg, ASM_STREAM_CMD_GET_PP_PARAMS_V2); + mutex_unlock(&rtac_asm_cal_mutex); break; case AUDIO_SET_RTAC_ASM_CAL: + mutex_lock(&rtac_asm_cal_mutex); result = send_rtac_asm_apr((void *)arg, ASM_STREAM_CMD_SET_PP_PARAMS_V2); + mutex_unlock(&rtac_asm_cal_mutex); break; case AUDIO_GET_RTAC_CVS_CAL: + mutex_lock(&rtac_voice_cal_mutex); result = send_voice_apr(RTAC_CVS, (void *) arg, VSS_ICOMMON_CMD_GET_PARAM_V2); + mutex_unlock(&rtac_voice_cal_mutex); break; case AUDIO_SET_RTAC_CVS_CAL: + mutex_lock(&rtac_voice_cal_mutex); result = send_voice_apr(RTAC_CVS, (void *) arg, VSS_ICOMMON_CMD_SET_PARAM_V2); + mutex_unlock(&rtac_voice_cal_mutex); break; case AUDIO_GET_RTAC_CVP_CAL: + mutex_lock(&rtac_voice_cal_mutex); result = send_voice_apr(RTAC_CVP, (void *) arg, VSS_ICOMMON_CMD_GET_PARAM_V2); + mutex_unlock(&rtac_voice_cal_mutex); break; case AUDIO_SET_RTAC_CVP_CAL: + mutex_lock(&rtac_voice_cal_mutex); result = send_voice_apr(RTAC_CVP, (void *) arg, VSS_ICOMMON_CMD_SET_PARAM_V2); + mutex_unlock(&rtac_voice_cal_mutex); break; case AUDIO_GET_RTAC_AFE_CAL: + mutex_lock(&rtac_afe_cal_mutex); result = send_rtac_afe_apr((void *)arg, AFE_PORT_CMD_GET_PARAM_V2); + mutex_unlock(&rtac_afe_cal_mutex); break; case AUDIO_SET_RTAC_AFE_CAL: + mutex_lock(&rtac_afe_cal_mutex); result = send_rtac_afe_apr((void *)arg, AFE_PORT_CMD_SET_PARAM_V2); + mutex_unlock(&rtac_afe_cal_mutex); break; default: pr_err("%s: Invalid IOCTL, command = %d!\n", @@ -1880,6 +1905,7 @@ static int __init rtac_init(void) init_waitqueue_head(&rtac_adm_apr_data.cmd_wait); mutex_init(&rtac_adm_mutex); mutex_init(&rtac_adm_apr_mutex); + mutex_init(&rtac_adm_cal_mutex); rtac_adm_buffer = kzalloc( rtac_cal[ADM_RTAC_CAL].map_data.map_size, GFP_KERNEL); @@ -1896,6 +1922,7 @@ static int __init rtac_init(void) init_waitqueue_head(&rtac_asm_apr_data[i].cmd_wait); } mutex_init(&rtac_asm_apr_mutex); + mutex_init(&rtac_asm_cal_mutex); rtac_asm_buffer = kzalloc( rtac_cal[ASM_RTAC_CAL].map_data.map_size, GFP_KERNEL); @@ -1911,6 +1938,7 @@ static int __init rtac_init(void) atomic_set(&rtac_afe_apr_data.cmd_state, 0); init_waitqueue_head(&rtac_afe_apr_data.cmd_wait); mutex_init(&rtac_afe_apr_mutex); + mutex_init(&rtac_afe_cal_mutex); rtac_afe_buffer = kzalloc( rtac_cal[AFE_RTAC_CAL].map_data.map_size, GFP_KERNEL); @@ -1931,6 +1959,7 @@ static int __init rtac_init(void) } mutex_init(&rtac_voice_mutex); mutex_init(&rtac_voice_apr_mutex); + mutex_init(&rtac_voice_cal_mutex); rtac_voice_buffer = kzalloc( rtac_cal[VOICE_RTAC_CAL].map_data.map_size, GFP_KERNEL); diff --git a/sound/soc/msm/sdm660-common.c b/sound/soc/msm/sdm660-common.c index 9eb58ec86409..8b979d405c7b 100644 --- a/sound/soc/msm/sdm660-common.c +++ b/sound/soc/msm/sdm660-common.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. * Copyright (C) 2018 XiaoMi, Inc. * * This program is free software; you can redistribute it and/or modify @@ -2565,11 +2565,9 @@ void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) mutex_lock(&mi2s_intf_conf[index].lock); if (--mi2s_intf_conf[index].ref_cnt == 0) { ret = msm_mi2s_set_sclk(substream, false); - if (ret < 0) { + if (ret < 0) pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n", __func__, index, ret); - mi2s_intf_conf[index].ref_cnt++; - } if (mi2s_intf_conf[index].msm_is_ext_mclk) { mi2s_mclk[index].enable = 0; pr_debug("%s: Disabling mclk, clk_freq_in_hz = %u\n", diff --git a/sound/usb/card.c b/sound/usb/card.c index 23ea575f3bcf..d0868a5395e0 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -316,6 +316,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) case UAC_VERSION_1: { void *control_header; struct uac1_ac_header_descriptor *h1; + int rest_bytes; control_header = snd_usb_find_csint_desc(host_iface->extra, host_iface->extralen, NULL, UAC_HEADER); @@ -325,11 +326,30 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) } h1 = control_header; + rest_bytes = (void *)(host_iface->extra + + host_iface->extralen) - control_header; + + /* just to be sure -- this shouldn't hit at all */ + if (rest_bytes <= 0) { + dev_err(&dev->dev, "invalid control header\n"); + return -EINVAL; + } + + if (rest_bytes < sizeof(*h1)) { + dev_err(&dev->dev, "too short v1 buffer descriptor\n"); + return -EINVAL; + } + if (!h1->bInCollection) { dev_info(&dev->dev, "skipping empty audio interface (v1)\n"); return -EINVAL; } + if (rest_bytes < h1->bLength) { + dev_err(&dev->dev, "invalid buffer length (v1)\n"); + return -EINVAL; + } + if (h1->bLength < sizeof(*h1) + h1->bInCollection) { dev_err(&dev->dev, "invalid UAC_HEADER (v1)\n"); return -EINVAL; diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 08c6bb2e5826..4fab7e7f9a62 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -2434,6 +2434,9 @@ static int parse_audio_unit(struct mixer_build *state, int unitid) static void snd_usb_mixer_free(struct usb_mixer_interface *mixer) { + /* kill pending URBs */ + snd_usb_mixer_disconnect(mixer); + kfree(mixer->id_elems); if (mixer->urb) { kfree(mixer->urb->transfer_buffer); @@ -2860,8 +2863,13 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif, void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer) { - usb_kill_urb(mixer->urb); - usb_kill_urb(mixer->rc_urb); + if (mixer->disconnected) + return; + if (mixer->urb) + usb_kill_urb(mixer->urb); + if (mixer->rc_urb) + usb_kill_urb(mixer->rc_urb); + mixer->disconnected = true; } #ifdef CONFIG_PM diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index 3417ef347e40..096eccb42d3e 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -22,6 +22,8 @@ struct usb_mixer_interface { struct urb *rc_urb; struct usb_ctrlrequest *rc_setup_packet; u8 rc_buffer[6]; + + bool disconnected; }; #define MAX_CHANNELS 16 /* max logical channels */