* Mon Dec 01 2025 Augusto Caringi <acaringi@redhat.com> [6.17.10-0] - Revert "gpio: swnode: don't use the swnode's name as the key for GPIO lookup" (Justin M. Forbes) - Linux v6.17.10 Resolves: Signed-off-by: Augusto Caringi <acaringi@redhat.com>
4067 lines
122 KiB
Diff
4067 lines
122 KiB
Diff
Documentation/arch/x86/tdx.rst | 14 +-
|
|
MAINTAINERS | 10 +
|
|
Makefile | 38 +-
|
|
arch/arm/Kconfig | 4 +-
|
|
arch/arm64/Kconfig | 2 +-
|
|
.../boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts | 143 ++++
|
|
arch/arm64/kernel/setup.c | 27 +
|
|
.../tools/gcc-check-fpatchable-function-entry.sh | 1 -
|
|
arch/powerpc/tools/gcc-check-mprofile-kernel.sh | 1 -
|
|
arch/s390/include/asm/ipl.h | 1 +
|
|
arch/s390/kernel/ipl.c | 5 +
|
|
arch/s390/kernel/setup.c | 4 +
|
|
arch/x86/Kconfig | 1 -
|
|
arch/x86/include/asm/kexec.h | 12 +-
|
|
arch/x86/include/asm/processor.h | 2 +
|
|
arch/x86/include/asm/tdx.h | 31 +-
|
|
arch/x86/kernel/cpu/amd.c | 17 +
|
|
arch/x86/kernel/machine_kexec_64.c | 44 +-
|
|
arch/x86/kernel/process.c | 24 +-
|
|
arch/x86/kernel/relocate_kernel_64.S | 36 +-
|
|
arch/x86/kernel/setup.c | 22 +-
|
|
arch/x86/kvm/vmx/tdx.c | 10 +
|
|
arch/x86/virt/vmx/tdx/tdx.c | 23 +-
|
|
crypto/sig.c | 3 +-
|
|
crypto/testmgr.c | 2 +-
|
|
drivers/acpi/apei/hest.c | 8 +
|
|
drivers/acpi/irq.c | 17 +-
|
|
drivers/acpi/scan.c | 9 +
|
|
drivers/ata/libahci.c | 18 +
|
|
drivers/char/ipmi/ipmi_dmi.c | 15 +
|
|
drivers/char/ipmi/ipmi_msghandler.c | 16 +-
|
|
drivers/firmware/efi/Makefile | 1 +
|
|
drivers/firmware/efi/efi.c | 124 +++-
|
|
drivers/firmware/efi/libstub/fdt.c | 5 +
|
|
drivers/firmware/efi/libstub/secureboot.c | 14 +-
|
|
drivers/firmware/efi/secureboot.c | 38 ++
|
|
drivers/gpio/Kconfig | 11 +
|
|
drivers/gpio/Makefile | 1 +
|
|
drivers/gpio/gpio-usbio.c | 248 +++++++
|
|
drivers/hid/hid-rmi.c | 66 --
|
|
drivers/hwtracing/coresight/coresight-etm4x-core.c | 19 +
|
|
drivers/i2c/busses/Kconfig | 11 +
|
|
drivers/i2c/busses/Makefile | 1 +
|
|
drivers/i2c/busses/i2c-usbio.c | 321 +++++++++
|
|
drivers/input/rmi4/rmi_driver.c | 124 ++--
|
|
drivers/iommu/iommu.c | 22 +
|
|
drivers/pci/quirks.c | 24 +
|
|
drivers/platform/x86/intel/int3472/discrete.c | 58 +-
|
|
drivers/scsi/sd.c | 10 +
|
|
drivers/usb/core/hub.c | 7 +
|
|
drivers/usb/misc/Kconfig | 14 +
|
|
drivers/usb/misc/Makefile | 1 +
|
|
drivers/usb/misc/usbio.c | 749 +++++++++++++++++++++
|
|
drivers/usb/typec/ucsi/ucsi.c | 6 +
|
|
include/linux/efi.h | 22 +-
|
|
include/linux/lsm_hook_defs.h | 1 +
|
|
include/linux/module.h | 1 +
|
|
include/linux/rmi.h | 1 +
|
|
include/linux/security.h | 9 +
|
|
include/linux/usb/usbio.h | 177 +++++
|
|
kernel/module/main.c | 2 +
|
|
kernel/module/signing.c | 9 +-
|
|
scripts/Makefile.lib | 3 +
|
|
scripts/mod/modpost.c | 8 +
|
|
scripts/tags.sh | 2 +
|
|
security/integrity/platform_certs/load_uefi.c | 6 +-
|
|
security/lockdown/Kconfig | 13 +
|
|
security/lockdown/lockdown.c | 11 +
|
|
tools/testing/selftests/bpf/Makefile | 2 +-
|
|
tools/testing/selftests/bpf/prog_tests/ksyms_btf.c | 31 -
|
|
70 files changed, 2448 insertions(+), 285 deletions(-)
|
|
|
|
diff --git a/Documentation/arch/x86/tdx.rst b/Documentation/arch/x86/tdx.rst
|
|
index 719043cd8b469..61670e7df2f7c 100644
|
|
--- a/Documentation/arch/x86/tdx.rst
|
|
+++ b/Documentation/arch/x86/tdx.rst
|
|
@@ -142,13 +142,6 @@ but depends on the BIOS to behave correctly.
|
|
Note TDX works with CPU logical online/offline, thus the kernel still
|
|
allows to offline logical CPU and online it again.
|
|
|
|
-Kexec()
|
|
-~~~~~~~
|
|
-
|
|
-TDX host support currently lacks the ability to handle kexec. For
|
|
-simplicity only one of them can be enabled in the Kconfig. This will be
|
|
-fixed in the future.
|
|
-
|
|
Erratum
|
|
~~~~~~~
|
|
|
|
@@ -171,6 +164,13 @@ If the platform has such erratum, the kernel prints additional message in
|
|
machine check handler to tell user the machine check may be caused by
|
|
kernel bug on TDX private memory.
|
|
|
|
+Kexec
|
|
+~~~~~~~
|
|
+
|
|
+Currently kexec doesn't work on the TDX platforms with the aforementioned
|
|
+erratum. It fails when loading the kexec kernel image. Otherwise it
|
|
+works normally.
|
|
+
|
|
Interaction vs S3 and deeper states
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
diff --git a/MAINTAINERS b/MAINTAINERS
|
|
index 97d958c945e4f..c7e27e2edfe45 100644
|
|
--- a/MAINTAINERS
|
|
+++ b/MAINTAINERS
|
|
@@ -12694,6 +12694,16 @@ S: Maintained
|
|
F: Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst
|
|
F: drivers/platform/x86/intel/uncore-frequency/
|
|
|
|
+INTEL USBIO USB I/O EXPANDER DRIVERS
|
|
+M: Israel Cepeda <israel.a.cepeda.lopez@intel.com>
|
|
+M: Hans de Goede <hansg@kernel.org>
|
|
+R: Sakari Ailus <sakari.ailus@linux.intel.com>
|
|
+S: Maintained
|
|
+F: drivers/gpio/gpio-usbio.c
|
|
+F: drivers/i2c/busses/i2c-usbio.c
|
|
+F: drivers/usb/misc/usbio.c
|
|
+F: include/linux/usb/usbio.h
|
|
+
|
|
INTEL VENDOR SPECIFIC EXTENDED CAPABILITIES DRIVER
|
|
M: David E. Box <david.e.box@linux.intel.com>
|
|
S: Supported
|
|
diff --git a/Makefile b/Makefile
|
|
index 2b8d9e95f50c1..d5501be55a48d 100644
|
|
--- a/Makefile
|
|
+++ b/Makefile
|
|
@@ -355,6 +355,17 @@ ifneq ($(filter install,$(MAKECMDGOALS)),)
|
|
endif
|
|
endif
|
|
|
|
+# CKI/cross compilation hack
|
|
+# Do we need to rebuild scripts after cross compilation?
|
|
+# If kernel was cross-compiled, these scripts have arch of build host.
|
|
+REBUILD_SCRIPTS_FOR_CROSS:=0
|
|
+
|
|
+# Regenerating config with incomplete source tree will produce different
|
|
+# config options. Disable it.
|
|
+ifeq ($(REBUILD_SCRIPTS_FOR_CROSS),1)
|
|
+may-sync-config:=
|
|
+endif
|
|
+
|
|
ifdef mixed-build
|
|
# ===========================================================================
|
|
# We're called with mixed targets (*config and build targets).
|
|
@@ -1306,6 +1317,8 @@ uapi-asm-generic:
|
|
# Generate some files
|
|
# ---------------------------------------------------------------------------
|
|
|
|
+include $(srctree)/Makefile.rhelver
|
|
+
|
|
# KERNELRELEASE can change from a few different places, meaning version.h
|
|
# needs to be updated, so this check is forced on all builds
|
|
|
|
@@ -1330,7 +1343,13 @@ define filechk_version.h
|
|
((c) > 255 ? 255 : (c)))'; \
|
|
echo \#define LINUX_VERSION_MAJOR $(VERSION); \
|
|
echo \#define LINUX_VERSION_PATCHLEVEL $(PATCHLEVEL); \
|
|
- echo \#define LINUX_VERSION_SUBLEVEL $(SUBLEVEL)
|
|
+ echo \#define LINUX_VERSION_SUBLEVEL $(SUBLEVEL); \
|
|
+ echo '#define RHEL_MAJOR $(RHEL_MAJOR)'; \
|
|
+ echo '#define RHEL_MINOR $(RHEL_MINOR)'; \
|
|
+ echo '#define RHEL_RELEASE_VERSION(a,b) (((a) << 8) + (b))'; \
|
|
+ echo '#define RHEL_RELEASE_CODE \
|
|
+ $(shell expr $(RHEL_MAJOR) \* 256 + $(RHEL_MINOR))'; \
|
|
+ echo '#define RHEL_RELEASE "$(RHEL_RELEASE)"'
|
|
endef
|
|
|
|
$(version_h): private PATCHLEVEL := $(or $(PATCHLEVEL), 0)
|
|
@@ -1932,6 +1951,23 @@ endif
|
|
|
|
ifdef CONFIG_MODULES
|
|
|
|
+scripts_build:
|
|
+ $(MAKE) $(build)=scripts/basic
|
|
+ $(MAKE) $(build)=scripts/mod
|
|
+ $(MAKE) $(build)=scripts scripts/module.lds
|
|
+ $(MAKE) $(build)=scripts scripts/unifdef
|
|
+ $(MAKE) $(build)=scripts
|
|
+
|
|
+prepare_after_cross:
|
|
+ # disable STACK_VALIDATION to avoid building objtool
|
|
+ sed -i '/^CONFIG_STACK_VALIDATION/d' ./include/config/auto.conf || true
|
|
+ # build minimum set of scripts and resolve_btfids to allow building
|
|
+ # external modules
|
|
+ $(MAKE) KBUILD_EXTMOD="" M="" scripts_build V=1
|
|
+ $(MAKE) -C tools/bpf/resolve_btfids
|
|
+
|
|
+PHONY += prepare_after_cross scripts_build
|
|
+
|
|
modules.order: $(build-dir)
|
|
@:
|
|
|
|
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
|
|
index b1f3df39ed406..5e1c1169e27e5 100644
|
|
--- a/arch/arm/Kconfig
|
|
+++ b/arch/arm/Kconfig
|
|
@@ -1230,9 +1230,9 @@ config HIGHMEM
|
|
If unsure, say n.
|
|
|
|
config HIGHPTE
|
|
- bool "Allocate 2nd-level pagetables from highmem" if EXPERT
|
|
+ bool "Allocate 2nd-level pagetables from highmem"
|
|
depends on HIGHMEM
|
|
- default y
|
|
+ default n
|
|
help
|
|
The VM uses one page of physical memory for each page table.
|
|
For systems with a lot of processes, this can use a lot of
|
|
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
|
|
index 93f391e67af15..237f7f623b4a3 100644
|
|
--- a/arch/arm64/Kconfig
|
|
+++ b/arch/arm64/Kconfig
|
|
@@ -1430,7 +1430,7 @@ endchoice
|
|
|
|
config ARM64_FORCE_52BIT
|
|
bool "Force 52-bit virtual addresses for userspace"
|
|
- depends on ARM64_VA_BITS_52 && EXPERT
|
|
+ depends on ARM64_VA_BITS_52
|
|
help
|
|
For systems with 52-bit userspace VAs enabled, the kernel will attempt
|
|
to maintain compatibility with older software by providing 48-bit VAs
|
|
diff --git a/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts b/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts
|
|
index dad0f11e8e858..d02f8d4f7baf0 100644
|
|
--- a/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts
|
|
+++ b/arch/arm64/boot/dts/qcom/x1e80100-lenovo-yoga-slim7x.dts
|
|
@@ -18,6 +18,7 @@ / {
|
|
|
|
aliases {
|
|
serial0 = &uart21;
|
|
+ serial1 = &uart14;
|
|
};
|
|
|
|
chosen {
|
|
@@ -404,6 +405,107 @@ vph_pwr: regulator-vph-pwr {
|
|
regulator-always-on;
|
|
regulator-boot-on;
|
|
};
|
|
+
|
|
+ vreg_wcn_3p3: regulator-wcn-3p3 {
|
|
+ compatible = "regulator-fixed";
|
|
+
|
|
+ regulator-name = "VREG_WCN_3P3";
|
|
+ regulator-min-microvolt = <3300000>;
|
|
+ regulator-max-microvolt = <3300000>;
|
|
+
|
|
+ gpio = <&tlmm 214 GPIO_ACTIVE_HIGH>;
|
|
+ enable-active-high;
|
|
+
|
|
+ pinctrl-0 = <&wcn_sw_en>;
|
|
+ pinctrl-names = "default";
|
|
+
|
|
+ regulator-boot-on;
|
|
+ };
|
|
+
|
|
+ /*
|
|
+ * TODO: These two regulators are actually part of the removable M.2
|
|
+ * card and not the CRD mainboard. Need to describe this differently.
|
|
+ * Functionally it works correctly, because all we need to do is to
|
|
+ * turn on the actual 3.3V supply above.
|
|
+ */
|
|
+ vreg_wcn_0p95: regulator-wcn-0p95 {
|
|
+ compatible = "regulator-fixed";
|
|
+
|
|
+ regulator-name = "VREG_WCN_0P95";
|
|
+ regulator-min-microvolt = <950000>;
|
|
+ regulator-max-microvolt = <950000>;
|
|
+
|
|
+ vin-supply = <&vreg_wcn_3p3>;
|
|
+ };
|
|
+
|
|
+ vreg_wcn_1p9: regulator-wcn-1p9 {
|
|
+ compatible = "regulator-fixed";
|
|
+
|
|
+ regulator-name = "VREG_WCN_1P9";
|
|
+ regulator-min-microvolt = <1900000>;
|
|
+ regulator-max-microvolt = <1900000>;
|
|
+
|
|
+ vin-supply = <&vreg_wcn_3p3>;
|
|
+ };
|
|
+
|
|
+ wcn7850-pmu {
|
|
+ compatible = "qcom,wcn7850-pmu";
|
|
+
|
|
+ vdd-supply = <&vreg_wcn_0p95>;
|
|
+ vddio-supply = <&vreg_l15b_1p8>;
|
|
+ vddaon-supply = <&vreg_wcn_0p95>;
|
|
+ vdddig-supply = <&vreg_wcn_0p95>;
|
|
+ vddrfa1p2-supply = <&vreg_wcn_1p9>;
|
|
+ vddrfa1p8-supply = <&vreg_wcn_1p9>;
|
|
+
|
|
+ wlan-enable-gpios = <&tlmm 117 GPIO_ACTIVE_HIGH>;
|
|
+ bt-enable-gpios = <&tlmm 116 GPIO_ACTIVE_HIGH>;
|
|
+
|
|
+ pinctrl-0 = <&wcn_wlan_bt_en>;
|
|
+ pinctrl-names = "default";
|
|
+
|
|
+ regulators {
|
|
+ vreg_pmu_rfa_cmn: ldo0 {
|
|
+ regulator-name = "vreg_pmu_rfa_cmn";
|
|
+ };
|
|
+
|
|
+ vreg_pmu_aon_0p59: ldo1 {
|
|
+ regulator-name = "vreg_pmu_aon_0p59";
|
|
+ };
|
|
+
|
|
+ vreg_pmu_wlcx_0p8: ldo2 {
|
|
+ regulator-name = "vreg_pmu_wlcx_0p8";
|
|
+ };
|
|
+
|
|
+ vreg_pmu_wlmx_0p85: ldo3 {
|
|
+ regulator-name = "vreg_pmu_wlmx_0p85";
|
|
+ };
|
|
+
|
|
+ vreg_pmu_btcmx_0p85: ldo4 {
|
|
+ regulator-name = "vreg_pmu_btcmx_0p85";
|
|
+ };
|
|
+
|
|
+ vreg_pmu_rfa_0p8: ldo5 {
|
|
+ regulator-name = "vreg_pmu_rfa_0p8";
|
|
+ };
|
|
+
|
|
+ vreg_pmu_rfa_1p2: ldo6 {
|
|
+ regulator-name = "vreg_pmu_rfa_1p2";
|
|
+ };
|
|
+
|
|
+ vreg_pmu_rfa_1p8: ldo7 {
|
|
+ regulator-name = "vreg_pmu_rfa_1p8";
|
|
+ };
|
|
+
|
|
+ vreg_pmu_pcie_0p9: ldo8 {
|
|
+ regulator-name = "vreg_pmu_pcie_0p9";
|
|
+ };
|
|
+
|
|
+ vreg_pmu_pcie_1p8: ldo9 {
|
|
+ regulator-name = "vreg_pmu_pcie_1p8";
|
|
+ };
|
|
+ };
|
|
+ };
|
|
};
|
|
|
|
&apps_rsc {
|
|
@@ -1045,6 +1147,16 @@ &pcie4_port0 {
|
|
wifi@0 {
|
|
compatible = "pci17cb,1107";
|
|
reg = <0x10000 0x0 0x0 0x0 0x0>;
|
|
+
|
|
+ vddaon-supply = <&vreg_pmu_aon_0p59>;
|
|
+ vddwlcx-supply = <&vreg_pmu_wlcx_0p8>;
|
|
+ vddwlmx-supply = <&vreg_pmu_wlmx_0p85>;
|
|
+ vddrfacmn-supply = <&vreg_pmu_rfa_cmn>;
|
|
+ vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>;
|
|
+ vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>;
|
|
+ vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>;
|
|
+ vddpcie0p9-supply = <&vreg_pmu_pcie_0p9>;
|
|
+ vddpcie1p8-supply = <&vreg_pmu_pcie_1p8>;
|
|
};
|
|
};
|
|
|
|
@@ -1403,6 +1515,37 @@ usb2_pwr_3p3_reg_en: usb2-pwr-3p3-reg-en-state {
|
|
drive-strength = <2>;
|
|
bias-disable;
|
|
};
|
|
+
|
|
+ wcn_sw_en: wcn-sw-en-state {
|
|
+ pins = "gpio214";
|
|
+ function = "gpio";
|
|
+ drive-strength = <2>;
|
|
+ bias-disable;
|
|
+ };
|
|
+
|
|
+ wcn_wlan_bt_en: wcn-wlan-bt-en-state {
|
|
+ pins = "gpio116", "gpio117";
|
|
+ function = "gpio";
|
|
+ drive-strength = <2>;
|
|
+ bias-disable;
|
|
+ };
|
|
+};
|
|
+
|
|
+&uart14 {
|
|
+ status = "okay";
|
|
+
|
|
+ bluetooth {
|
|
+ compatible = "qcom,wcn7850-bt";
|
|
+ max-speed = <3200000>;
|
|
+
|
|
+ vddaon-supply = <&vreg_pmu_aon_0p59>;
|
|
+ vddwlcx-supply = <&vreg_pmu_wlcx_0p8>;
|
|
+ vddwlmx-supply = <&vreg_pmu_wlmx_0p85>;
|
|
+ vddrfacmn-supply = <&vreg_pmu_rfa_cmn>;
|
|
+ vddrfa0p8-supply = <&vreg_pmu_rfa_0p8>;
|
|
+ vddrfa1p2-supply = <&vreg_pmu_rfa_1p2>;
|
|
+ vddrfa1p8-supply = <&vreg_pmu_rfa_1p8>;
|
|
+ };
|
|
};
|
|
|
|
&uart21 {
|
|
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
|
|
index 23c05dc7a8f2a..d7b7b2f39e169 100644
|
|
--- a/arch/arm64/kernel/setup.c
|
|
+++ b/arch/arm64/kernel/setup.c
|
|
@@ -32,6 +32,8 @@
|
|
#include <linux/sched/task.h>
|
|
#include <linux/scs.h>
|
|
#include <linux/mm.h>
|
|
+#include <linux/security.h>
|
|
+#include <linux/libfdt.h>
|
|
|
|
#include <asm/acpi.h>
|
|
#include <asm/fixmap.h>
|
|
@@ -207,6 +209,24 @@ static void __init setup_machine_fdt(phys_addr_t dt_phys)
|
|
dump_stack_set_arch_desc("%s (DT)", name);
|
|
}
|
|
|
|
+static void __init init_secureboot_mode(void)
|
|
+{
|
|
+ void *fdt = initial_boot_params;
|
|
+ u64 chosen;
|
|
+ const __be32 *prop;
|
|
+ int len;
|
|
+
|
|
+ chosen = fdt_path_offset(fdt, "/chosen");
|
|
+ if (chosen < 0)
|
|
+ return;
|
|
+
|
|
+ prop = fdt_getprop(fdt, chosen, "secure-boot-mode", &len);
|
|
+ if (!prop || len != sizeof(u32))
|
|
+ return;
|
|
+
|
|
+ efi_set_secure_boot((enum efi_secureboot_mode)fdt32_to_cpu(*prop));
|
|
+}
|
|
+
|
|
static void __init request_standard_resources(void)
|
|
{
|
|
struct memblock_region *region;
|
|
@@ -327,6 +347,13 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p)
|
|
pr_warn(FW_BUG "Kernel image misaligned at boot, please fix your bootloader!");
|
|
WARN_TAINT(mmu_enabled_at_boot, TAINT_FIRMWARE_WORKAROUND,
|
|
FW_BUG "Booted with MMU enabled!");
|
|
+ } else {
|
|
+ init_secureboot_mode();
|
|
+
|
|
+#ifdef CONFIG_LOCK_DOWN_IN_EFI_SECURE_BOOT
|
|
+ if (efi_enabled(EFI_SECURE_BOOT))
|
|
+ security_lock_kernel_down("EFI Secure Boot mode", LOCKDOWN_INTEGRITY_MAX);
|
|
+#endif
|
|
}
|
|
|
|
arm64_memblock_init();
|
|
diff --git a/arch/powerpc/tools/gcc-check-fpatchable-function-entry.sh b/arch/powerpc/tools/gcc-check-fpatchable-function-entry.sh
|
|
index 06706903503b6..baed467a016b3 100755
|
|
--- a/arch/powerpc/tools/gcc-check-fpatchable-function-entry.sh
|
|
+++ b/arch/powerpc/tools/gcc-check-fpatchable-function-entry.sh
|
|
@@ -2,7 +2,6 @@
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
|
|
set -e
|
|
-set -o pipefail
|
|
|
|
# To debug, uncomment the following line
|
|
# set -x
|
|
diff --git a/arch/powerpc/tools/gcc-check-mprofile-kernel.sh b/arch/powerpc/tools/gcc-check-mprofile-kernel.sh
|
|
index 73e331e7660ef..6193b0ed0c775 100755
|
|
--- a/arch/powerpc/tools/gcc-check-mprofile-kernel.sh
|
|
+++ b/arch/powerpc/tools/gcc-check-mprofile-kernel.sh
|
|
@@ -2,7 +2,6 @@
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
|
|
set -e
|
|
-set -o pipefail
|
|
|
|
# To debug, uncomment the following line
|
|
# set -x
|
|
diff --git a/arch/s390/include/asm/ipl.h b/arch/s390/include/asm/ipl.h
|
|
index b0d00032479d6..afb9544fb0074 100644
|
|
--- a/arch/s390/include/asm/ipl.h
|
|
+++ b/arch/s390/include/asm/ipl.h
|
|
@@ -139,6 +139,7 @@ int ipl_report_add_component(struct ipl_report *report, struct kexec_buf *kbuf,
|
|
unsigned char flags, unsigned short cert);
|
|
int ipl_report_add_certificate(struct ipl_report *report, void *key,
|
|
unsigned long addr, unsigned long len);
|
|
+bool ipl_get_secureboot(void);
|
|
|
|
/*
|
|
* DIAG 308 support
|
|
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c
|
|
index 961a3d60a4ddd..927ba8a7b3ac0 100644
|
|
--- a/arch/s390/kernel/ipl.c
|
|
+++ b/arch/s390/kernel/ipl.c
|
|
@@ -2497,3 +2497,8 @@ int ipl_report_free(struct ipl_report *report)
|
|
}
|
|
|
|
#endif
|
|
+
|
|
+bool ipl_get_secureboot(void)
|
|
+{
|
|
+ return !!ipl_secure_flag;
|
|
+}
|
|
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
|
|
index 7b529868789f9..c054a407afa68 100644
|
|
--- a/arch/s390/kernel/setup.c
|
|
+++ b/arch/s390/kernel/setup.c
|
|
@@ -49,6 +49,7 @@
|
|
#include <linux/memory.h>
|
|
#include <linux/compat.h>
|
|
#include <linux/start_kernel.h>
|
|
+#include <linux/security.h>
|
|
#include <linux/hugetlb.h>
|
|
#include <linux/kmemleak.h>
|
|
|
|
@@ -919,6 +920,9 @@ void __init setup_arch(char **cmdline_p)
|
|
|
|
log_component_list();
|
|
|
|
+ if (ipl_get_secureboot())
|
|
+ security_lock_kernel_down("Secure IPL mode", LOCKDOWN_INTEGRITY_MAX);
|
|
+
|
|
/* Have one command line that is parsed and saved in /proc/cmdline */
|
|
/* boot_command_line has been already set up in early.c */
|
|
*cmdline_p = boot_command_line;
|
|
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
|
|
index 05880301212e3..5902dde9f4477 100644
|
|
--- a/arch/x86/Kconfig
|
|
+++ b/arch/x86/Kconfig
|
|
@@ -1896,7 +1896,6 @@ config INTEL_TDX_HOST
|
|
depends on X86_X2APIC
|
|
select ARCH_KEEP_MEMBLOCK
|
|
depends on CONTIG_ALLOC
|
|
- depends on !KEXEC_CORE
|
|
depends on X86_MCE
|
|
help
|
|
Intel Trust Domain Extensions (TDX) protects guest VMs from malicious
|
|
diff --git a/arch/x86/include/asm/kexec.h b/arch/x86/include/asm/kexec.h
|
|
index f2ad77929d6ef..5cfb27f26583c 100644
|
|
--- a/arch/x86/include/asm/kexec.h
|
|
+++ b/arch/x86/include/asm/kexec.h
|
|
@@ -13,6 +13,15 @@
|
|
# define KEXEC_DEBUG_EXC_HANDLER_SIZE 6 /* PUSHI, PUSHI, 2-byte JMP */
|
|
#endif
|
|
|
|
+#ifdef CONFIG_X86_64
|
|
+
|
|
+#include <linux/bits.h>
|
|
+
|
|
+#define RELOC_KERNEL_PRESERVE_CONTEXT BIT(0)
|
|
+#define RELOC_KERNEL_CACHE_INCOHERENT BIT(1)
|
|
+
|
|
+#endif
|
|
+
|
|
# define KEXEC_CONTROL_PAGE_SIZE 4096
|
|
# define KEXEC_CONTROL_CODE_MAX_SIZE 2048
|
|
|
|
@@ -121,8 +130,7 @@ typedef unsigned long
|
|
relocate_kernel_fn(unsigned long indirection_page,
|
|
unsigned long pa_control_page,
|
|
unsigned long start_address,
|
|
- unsigned int preserve_context,
|
|
- unsigned int host_mem_enc_active);
|
|
+ unsigned int flags);
|
|
#endif
|
|
extern relocate_kernel_fn relocate_kernel;
|
|
#define ARCH_HAS_KIMAGE_ARCH
|
|
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
|
|
index bde58f6510ac4..a24c7805acdb5 100644
|
|
--- a/arch/x86/include/asm/processor.h
|
|
+++ b/arch/x86/include/asm/processor.h
|
|
@@ -731,6 +731,8 @@ void __noreturn stop_this_cpu(void *dummy);
|
|
void microcode_check(struct cpuinfo_x86 *prev_info);
|
|
void store_cpu_caps(struct cpuinfo_x86 *info);
|
|
|
|
+DECLARE_PER_CPU(bool, cache_state_incoherent);
|
|
+
|
|
enum l1tf_mitigations {
|
|
L1TF_MITIGATION_OFF,
|
|
L1TF_MITIGATION_AUTO,
|
|
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
|
|
index 5e043961fb1d7..dc9b78ce0e596 100644
|
|
--- a/arch/x86/include/asm/tdx.h
|
|
+++ b/arch/x86/include/asm/tdx.h
|
|
@@ -102,10 +102,31 @@ u64 __seamcall_ret(u64 fn, struct tdx_module_args *args);
|
|
u64 __seamcall_saved_ret(u64 fn, struct tdx_module_args *args);
|
|
void tdx_init(void);
|
|
|
|
+#include <linux/preempt.h>
|
|
#include <asm/archrandom.h>
|
|
+#include <asm/processor.h>
|
|
|
|
typedef u64 (*sc_func_t)(u64 fn, struct tdx_module_args *args);
|
|
|
|
+static __always_inline u64 __seamcall_dirty_cache(sc_func_t func, u64 fn,
|
|
+ struct tdx_module_args *args)
|
|
+{
|
|
+ lockdep_assert_preemption_disabled();
|
|
+
|
|
+ /*
|
|
+ * SEAMCALLs are made to the TDX module and can generate dirty
|
|
+ * cachelines of TDX private memory. Mark cache state incoherent
|
|
+ * so that the cache can be flushed during kexec.
|
|
+ *
|
|
+ * This needs to be done before actually making the SEAMCALL,
|
|
+ * because kexec-ing CPU could send NMI to stop remote CPUs,
|
|
+ * in which case even disabling IRQ won't help here.
|
|
+ */
|
|
+ this_cpu_write(cache_state_incoherent, true);
|
|
+
|
|
+ return func(fn, args);
|
|
+}
|
|
+
|
|
static __always_inline u64 sc_retry(sc_func_t func, u64 fn,
|
|
struct tdx_module_args *args)
|
|
{
|
|
@@ -113,7 +134,9 @@ static __always_inline u64 sc_retry(sc_func_t func, u64 fn,
|
|
u64 ret;
|
|
|
|
do {
|
|
- ret = func(fn, args);
|
|
+ preempt_disable();
|
|
+ ret = __seamcall_dirty_cache(func, fn, args);
|
|
+ preempt_enable();
|
|
} while (ret == TDX_RND_NO_ENTROPY && --retry);
|
|
|
|
return ret;
|
|
@@ -205,5 +228,11 @@ static inline const char *tdx_dump_mce_info(struct mce *m) { return NULL; }
|
|
static inline const struct tdx_sys_info *tdx_get_sysinfo(void) { return NULL; }
|
|
#endif /* CONFIG_INTEL_TDX_HOST */
|
|
|
|
+#ifdef CONFIG_KEXEC_CORE
|
|
+void tdx_cpu_flush_cache_for_kexec(void);
|
|
+#else
|
|
+static inline void tdx_cpu_flush_cache_for_kexec(void) { }
|
|
+#endif
|
|
+
|
|
#endif /* !__ASSEMBLER__ */
|
|
#endif /* _ASM_X86_TDX_H */
|
|
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
|
|
index 9390312c93b6e..5d46709c58d0b 100644
|
|
--- a/arch/x86/kernel/cpu/amd.c
|
|
+++ b/arch/x86/kernel/cpu/amd.c
|
|
@@ -545,6 +545,23 @@ static void early_detect_mem_encrypt(struct cpuinfo_x86 *c)
|
|
{
|
|
u64 msr;
|
|
|
|
+ /*
|
|
+ * Mark using WBINVD is needed during kexec on processors that
|
|
+ * support SME. This provides support for performing a successful
|
|
+ * kexec when going from SME inactive to SME active (or vice-versa).
|
|
+ *
|
|
+ * The cache must be cleared so that if there are entries with the
|
|
+ * same physical address, both with and without the encryption bit,
|
|
+ * they don't race each other when flushed and potentially end up
|
|
+ * with the wrong entry being committed to memory.
|
|
+ *
|
|
+ * Test the CPUID bit directly because with mem_encrypt=off the
|
|
+ * BSP will clear the X86_FEATURE_SME bit and the APs will not
|
|
+ * see it set after that.
|
|
+ */
|
|
+ if (c->extended_cpuid_level >= 0x8000001f && (cpuid_eax(0x8000001f) & BIT(0)))
|
|
+ __this_cpu_write(cache_state_incoherent, true);
|
|
+
|
|
/*
|
|
* BIOS support is required for SME and SEV.
|
|
* For SME: If BIOS has enabled SME then adjust x86_phys_bits by
|
|
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
|
|
index 697fb99406e6b..15088d14904fc 100644
|
|
--- a/arch/x86/kernel/machine_kexec_64.c
|
|
+++ b/arch/x86/kernel/machine_kexec_64.c
|
|
@@ -29,6 +29,7 @@
|
|
#include <asm/set_memory.h>
|
|
#include <asm/cpu.h>
|
|
#include <asm/efi.h>
|
|
+#include <asm/processor.h>
|
|
|
|
#ifdef CONFIG_ACPI
|
|
/*
|
|
@@ -346,6 +347,22 @@ int machine_kexec_prepare(struct kimage *image)
|
|
unsigned long reloc_end = (unsigned long)__relocate_kernel_end;
|
|
int result;
|
|
|
|
+ /*
|
|
+ * Some early TDX-capable platforms have an erratum. A kernel
|
|
+ * partial write (a write transaction of less than cacheline
|
|
+ * lands at memory controller) to TDX private memory poisons that
|
|
+ * memory, and a subsequent read triggers a machine check.
|
|
+ *
|
|
+ * On those platforms the old kernel must reset TDX private
|
|
+ * memory before jumping to the new kernel otherwise the new
|
|
+ * kernel may see unexpected machine check. For simplicity
|
|
+ * just fail kexec/kdump on those platforms.
|
|
+ */
|
|
+ if (boot_cpu_has_bug(X86_BUG_TDX_PW_MCE)) {
|
|
+ pr_info_once("Not allowed on platform with tdx_pw_mce bug\n");
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
/* Setup the identity mapped 64bit page table */
|
|
result = init_pgtable(image, __pa(control_page));
|
|
if (result)
|
|
@@ -384,16 +401,10 @@ void __nocfi machine_kexec(struct kimage *image)
|
|
{
|
|
unsigned long reloc_start = (unsigned long)__relocate_kernel_start;
|
|
relocate_kernel_fn *relocate_kernel_ptr;
|
|
- unsigned int host_mem_enc_active;
|
|
+ unsigned int relocate_kernel_flags;
|
|
int save_ftrace_enabled;
|
|
void *control_page;
|
|
|
|
- /*
|
|
- * This must be done before load_segments() since if call depth tracking
|
|
- * is used then GS must be valid to make any function calls.
|
|
- */
|
|
- host_mem_enc_active = cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT);
|
|
-
|
|
#ifdef CONFIG_KEXEC_JUMP
|
|
if (image->preserve_context)
|
|
save_processor_state();
|
|
@@ -427,6 +438,17 @@ void __nocfi machine_kexec(struct kimage *image)
|
|
*/
|
|
relocate_kernel_ptr = control_page + (unsigned long)relocate_kernel - reloc_start;
|
|
|
|
+ relocate_kernel_flags = 0;
|
|
+ if (image->preserve_context)
|
|
+ relocate_kernel_flags |= RELOC_KERNEL_PRESERVE_CONTEXT;
|
|
+
|
|
+ /*
|
|
+ * This must be done before load_segments() since it resets
|
|
+ * GS to 0 and percpu data needs the correct GS to work.
|
|
+ */
|
|
+ if (this_cpu_read(cache_state_incoherent))
|
|
+ relocate_kernel_flags |= RELOC_KERNEL_CACHE_INCOHERENT;
|
|
+
|
|
/*
|
|
* The segment registers are funny things, they have both a
|
|
* visible and an invisible part. Whenever the visible part is
|
|
@@ -436,6 +458,11 @@ void __nocfi machine_kexec(struct kimage *image)
|
|
*
|
|
* Take advantage of this here by force loading the segments,
|
|
* before the GDT is zapped with an invalid value.
|
|
+ *
|
|
+ * load_segments() resets GS to 0. Don't make any function call
|
|
+ * after here since call depth tracking uses percpu variables to
|
|
+ * operate (relocate_kernel() is explicitly ignored by call depth
|
|
+ * tracking).
|
|
*/
|
|
load_segments();
|
|
|
|
@@ -443,8 +470,7 @@ void __nocfi machine_kexec(struct kimage *image)
|
|
image->start = relocate_kernel_ptr((unsigned long)image->head,
|
|
virt_to_phys(control_page),
|
|
image->start,
|
|
- image->preserve_context,
|
|
- host_mem_enc_active);
|
|
+ relocate_kernel_flags);
|
|
|
|
#ifdef CONFIG_KEXEC_JUMP
|
|
if (image->preserve_context)
|
|
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
|
|
index e3a3987b0c4fb..4c718f8adc592 100644
|
|
--- a/arch/x86/kernel/process.c
|
|
+++ b/arch/x86/kernel/process.c
|
|
@@ -88,6 +88,16 @@ EXPORT_PER_CPU_SYMBOL(cpu_tss_rw);
|
|
DEFINE_PER_CPU(bool, __tss_limit_invalid);
|
|
EXPORT_PER_CPU_SYMBOL_GPL(__tss_limit_invalid);
|
|
|
|
+/*
|
|
+ * The cache may be in an incoherent state and needs flushing during kexec.
|
|
+ * E.g., on SME/TDX platforms, dirty cacheline aliases with and without
|
|
+ * encryption bit(s) can coexist and the cache needs to be flushed before
|
|
+ * booting to the new kernel to avoid the silent memory corruption due to
|
|
+ * dirty cachelines with different encryption property being written back
|
|
+ * to the memory.
|
|
+ */
|
|
+DEFINE_PER_CPU(bool, cache_state_incoherent);
|
|
+
|
|
/*
|
|
* this gets called so that we can store lazy state into memory and copy the
|
|
* current task into the new thread.
|
|
@@ -827,19 +837,7 @@ void __noreturn stop_this_cpu(void *dummy)
|
|
disable_local_APIC();
|
|
mcheck_cpu_clear(c);
|
|
|
|
- /*
|
|
- * Use wbinvd on processors that support SME. This provides support
|
|
- * for performing a successful kexec when going from SME inactive
|
|
- * to SME active (or vice-versa). The cache must be cleared so that
|
|
- * if there are entries with the same physical address, both with and
|
|
- * without the encryption bit, they don't race each other when flushed
|
|
- * and potentially end up with the wrong entry being committed to
|
|
- * memory.
|
|
- *
|
|
- * Test the CPUID bit directly because the machine might've cleared
|
|
- * X86_FEATURE_SME due to cmdline options.
|
|
- */
|
|
- if (c->extended_cpuid_level >= 0x8000001f && (cpuid_eax(0x8000001f) & BIT(0)))
|
|
+ if (this_cpu_read(cache_state_incoherent))
|
|
wbinvd();
|
|
|
|
/*
|
|
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
|
|
index ea604f4d0b52b..11e20bb13acaa 100644
|
|
--- a/arch/x86/kernel/relocate_kernel_64.S
|
|
+++ b/arch/x86/kernel/relocate_kernel_64.S
|
|
@@ -66,8 +66,7 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
|
|
* %rdi indirection_page
|
|
* %rsi pa_control_page
|
|
* %rdx start address
|
|
- * %rcx preserve_context
|
|
- * %r8 host_mem_enc_active
|
|
+ * %rcx flags: RELOC_KERNEL_*
|
|
*/
|
|
|
|
/* Save the CPU context, used for jumping back */
|
|
@@ -111,7 +110,7 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
|
|
/* save indirection list for jumping back */
|
|
movq %rdi, pa_backup_pages_map(%rip)
|
|
|
|
- /* Save the preserve_context to %r11 as swap_pages clobbers %rcx. */
|
|
+ /* Save the flags to %r11 as swap_pages clobbers %rcx. */
|
|
movq %rcx, %r11
|
|
|
|
/* setup a new stack at the end of the physical control page */
|
|
@@ -129,9 +128,8 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
|
|
/*
|
|
* %rdi indirection page
|
|
* %rdx start address
|
|
- * %r8 host_mem_enc_active
|
|
* %r9 page table page
|
|
- * %r11 preserve_context
|
|
+ * %r11 flags: RELOC_KERNEL_*
|
|
* %r13 original CR4 when relocate_kernel() was invoked
|
|
*/
|
|
|
|
@@ -200,14 +198,21 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
|
|
movq %r9, %cr3
|
|
|
|
/*
|
|
+ * If the memory cache is in incoherent state, e.g., due to
|
|
+ * memory encryption, do WBINVD to flush cache.
|
|
+ *
|
|
* If SME is active, there could be old encrypted cache line
|
|
* entries that will conflict with the now unencrypted memory
|
|
* used by kexec. Flush the caches before copying the kernel.
|
|
+ *
|
|
+ * Note SME sets this flag to true when the platform supports
|
|
+ * SME, so the WBINVD is performed even SME is not activated
|
|
+ * by the kernel. But this has no harm.
|
|
*/
|
|
- testq %r8, %r8
|
|
- jz .Lsme_off
|
|
+ testb $RELOC_KERNEL_CACHE_INCOHERENT, %r11b
|
|
+ jz .Lnowbinvd
|
|
wbinvd
|
|
-.Lsme_off:
|
|
+.Lnowbinvd:
|
|
|
|
call swap_pages
|
|
|
|
@@ -220,7 +225,7 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
|
|
movq %cr3, %rax
|
|
movq %rax, %cr3
|
|
|
|
- testq %r11, %r11 /* preserve_context */
|
|
+ testb $RELOC_KERNEL_PRESERVE_CONTEXT, %r11b
|
|
jnz .Lrelocate
|
|
|
|
/*
|
|
@@ -273,7 +278,13 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
|
|
ANNOTATE_NOENDBR
|
|
andq $PAGE_MASK, %r8
|
|
lea PAGE_SIZE(%r8), %rsp
|
|
- movl $1, %r11d /* Ensure preserve_context flag is set */
|
|
+ /*
|
|
+ * Ensure RELOC_KERNEL_PRESERVE_CONTEXT flag is set so that
|
|
+ * swap_pages() can swap pages correctly. Note all other
|
|
+ * RELOC_KERNEL_* flags passed to relocate_kernel() are not
|
|
+ * restored.
|
|
+ */
|
|
+ movl $RELOC_KERNEL_PRESERVE_CONTEXT, %r11d
|
|
call swap_pages
|
|
movq kexec_va_control_page(%rip), %rax
|
|
0: addq $virtual_mapped - 0b, %rax
|
|
@@ -321,7 +332,7 @@ SYM_CODE_START_LOCAL_NOALIGN(swap_pages)
|
|
UNWIND_HINT_END_OF_STACK
|
|
/*
|
|
* %rdi indirection page
|
|
- * %r11 preserve_context
|
|
+ * %r11 flags: RELOC_KERNEL_*
|
|
*/
|
|
movq %rdi, %rcx /* Put the indirection_page in %rcx */
|
|
xorl %edi, %edi
|
|
@@ -357,7 +368,8 @@ SYM_CODE_START_LOCAL_NOALIGN(swap_pages)
|
|
movq %rdi, %rdx /* Save destination page to %rdx */
|
|
movq %rsi, %rax /* Save source page to %rax */
|
|
|
|
- testq %r11, %r11 /* Only actually swap for ::preserve_context */
|
|
+ /* Only actually swap for ::preserve_context */
|
|
+ testb $RELOC_KERNEL_PRESERVE_CONTEXT, %r11b
|
|
jz .Lnoswap
|
|
|
|
/* copy source page to swap page */
|
|
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
|
|
index 1b2edd07a3e17..1c434c6900ebe 100644
|
|
--- a/arch/x86/kernel/setup.c
|
|
+++ b/arch/x86/kernel/setup.c
|
|
@@ -21,6 +21,7 @@
|
|
#include <linux/pci.h>
|
|
#include <linux/random.h>
|
|
#include <linux/root_dev.h>
|
|
+#include <linux/security.h>
|
|
#include <linux/static_call.h>
|
|
#include <linux/swiotlb.h>
|
|
#include <linux/tboot.h>
|
|
@@ -991,6 +992,13 @@ void __init setup_arch(char **cmdline_p)
|
|
if (efi_enabled(EFI_BOOT))
|
|
efi_init();
|
|
|
|
+ efi_set_secure_boot(boot_params.secure_boot);
|
|
+
|
|
+#ifdef CONFIG_LOCK_DOWN_IN_EFI_SECURE_BOOT
|
|
+ if (efi_enabled(EFI_SECURE_BOOT))
|
|
+ security_lock_kernel_down("EFI Secure Boot mode", LOCKDOWN_INTEGRITY_MAX);
|
|
+#endif
|
|
+
|
|
reserve_ibft_region();
|
|
x86_init.resources.dmi_setup();
|
|
|
|
@@ -1154,19 +1162,7 @@ void __init setup_arch(char **cmdline_p)
|
|
/* Allocate bigger log buffer */
|
|
setup_log_buf(1);
|
|
|
|
- if (efi_enabled(EFI_BOOT)) {
|
|
- switch (boot_params.secure_boot) {
|
|
- case efi_secureboot_mode_disabled:
|
|
- pr_info("Secure boot disabled\n");
|
|
- break;
|
|
- case efi_secureboot_mode_enabled:
|
|
- pr_info("Secure boot enabled\n");
|
|
- break;
|
|
- default:
|
|
- pr_info("Secure boot could not be determined\n");
|
|
- break;
|
|
- }
|
|
- }
|
|
+ efi_set_secure_boot(boot_params.secure_boot);
|
|
|
|
reserve_initrd();
|
|
|
|
diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
|
|
index 987c0eb10545c..adeb59155074f 100644
|
|
--- a/arch/x86/kvm/vmx/tdx.c
|
|
+++ b/arch/x86/kvm/vmx/tdx.c
|
|
@@ -442,6 +442,16 @@ void tdx_disable_virtualization_cpu(void)
|
|
tdx_flush_vp(&arg);
|
|
}
|
|
local_irq_restore(flags);
|
|
+
|
|
+ /*
|
|
+ * Flush cache now if kexec is possible: this is necessary to avoid
|
|
+ * having dirty private memory cachelines when the new kernel boots,
|
|
+ * but WBINVD is a relatively expensive operation and doing it during
|
|
+ * kexec can exacerbate races in native_stop_other_cpus(). Do it
|
|
+ * now, since this is a safe moment and there is going to be no more
|
|
+ * TDX activity on this CPU from this point on.
|
|
+ */
|
|
+ tdx_cpu_flush_cache_for_kexec();
|
|
}
|
|
|
|
#define TDX_SEAMCALL_RETRIES 10000
|
|
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
|
|
index 9767b5821f4d8..115ed29a8d2e1 100644
|
|
--- a/arch/x86/virt/vmx/tdx/tdx.c
|
|
+++ b/arch/x86/virt/vmx/tdx/tdx.c
|
|
@@ -1266,7 +1266,7 @@ static bool paddr_is_tdx_private(unsigned long phys)
|
|
return false;
|
|
|
|
/* Get page type from the TDX module */
|
|
- sret = __seamcall_ret(TDH_PHYMEM_PAGE_RDMD, &args);
|
|
+ sret = __seamcall_dirty_cache(__seamcall_ret, TDH_PHYMEM_PAGE_RDMD, &args);
|
|
|
|
/*
|
|
* The SEAMCALL will not return success unless there is a
|
|
@@ -1517,7 +1517,7 @@ noinstr u64 tdh_vp_enter(struct tdx_vp *td, struct tdx_module_args *args)
|
|
{
|
|
args->rcx = td->tdvpr_pa;
|
|
|
|
- return __seamcall_saved_ret(TDH_VP_ENTER, args);
|
|
+ return __seamcall_dirty_cache(__seamcall_saved_ret, TDH_VP_ENTER, args);
|
|
}
|
|
EXPORT_SYMBOL_GPL(tdh_vp_enter);
|
|
|
|
@@ -1865,3 +1865,22 @@ u64 tdh_phymem_page_wbinvd_hkid(u64 hkid, struct page *page)
|
|
return seamcall(TDH_PHYMEM_PAGE_WBINVD, &args);
|
|
}
|
|
EXPORT_SYMBOL_GPL(tdh_phymem_page_wbinvd_hkid);
|
|
+
|
|
+#ifdef CONFIG_KEXEC_CORE
|
|
+void tdx_cpu_flush_cache_for_kexec(void)
|
|
+{
|
|
+ lockdep_assert_preemption_disabled();
|
|
+
|
|
+ if (!this_cpu_read(cache_state_incoherent))
|
|
+ return;
|
|
+
|
|
+ /*
|
|
+ * Private memory cachelines need to be clean at the time of
|
|
+ * kexec. Write them back now, as the caller promises that
|
|
+ * there should be no more SEAMCALLs on this CPU.
|
|
+ */
|
|
+ wbinvd();
|
|
+ this_cpu_write(cache_state_incoherent, false);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(tdx_cpu_flush_cache_for_kexec);
|
|
+#endif
|
|
diff --git a/crypto/sig.c b/crypto/sig.c
|
|
index beba745b64057..fd41f6d3abf9a 100644
|
|
--- a/crypto/sig.c
|
|
+++ b/crypto/sig.c
|
|
@@ -112,8 +112,7 @@ static int sig_prepare_alg(struct sig_alg *alg)
|
|
{
|
|
struct crypto_alg *base = &alg->base;
|
|
|
|
- if (!alg->sign)
|
|
- alg->sign = sig_default_sign;
|
|
+ alg->sign = sig_default_sign;
|
|
if (!alg->verify)
|
|
alg->verify = sig_default_verify;
|
|
if (!alg->set_priv_key)
|
|
diff --git a/crypto/testmgr.c b/crypto/testmgr.c
|
|
index 3e284706152aa..9195f7d539dc7 100644
|
|
--- a/crypto/testmgr.c
|
|
+++ b/crypto/testmgr.c
|
|
@@ -4070,7 +4070,7 @@ static int test_sig_one(struct crypto_sig *tfm, const struct sig_testvec *vecs)
|
|
* Don't invoke sign test (which requires a private key)
|
|
* for vectors with only a public key.
|
|
*/
|
|
- if (vecs->public_key_vec)
|
|
+ if (1 || vecs->public_key_vec)
|
|
return 0;
|
|
|
|
sig_size = crypto_sig_maxsize(tfm);
|
|
diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c
|
|
index 20d757687e3d9..90a13f20f052b 100644
|
|
--- a/drivers/acpi/apei/hest.c
|
|
+++ b/drivers/acpi/apei/hest.c
|
|
@@ -142,6 +142,14 @@ static int apei_hest_parse(apei_hest_func_t func, void *data)
|
|
if (hest_disable || !hest_tab)
|
|
return -EINVAL;
|
|
|
|
+#ifdef CONFIG_ARM64
|
|
+ /* Ignore broken firmware */
|
|
+ if (!strncmp(hest_tab->header.oem_id, "HPE ", 6) &&
|
|
+ !strncmp(hest_tab->header.oem_table_id, "ProLiant", 8) &&
|
|
+ MIDR_IMPLEMENTOR(read_cpuid_id()) == ARM_CPU_IMP_APM)
|
|
+ return -EINVAL;
|
|
+#endif
|
|
+
|
|
hest_hdr = (struct acpi_hest_header *)(hest_tab + 1);
|
|
for (i = 0; i < hest_tab->error_source_count; i++) {
|
|
len = hest_esrc_len(hest_hdr);
|
|
diff --git a/drivers/acpi/irq.c b/drivers/acpi/irq.c
|
|
index 76a856c32c4d0..f2d25d95811c9 100644
|
|
--- a/drivers/acpi/irq.c
|
|
+++ b/drivers/acpi/irq.c
|
|
@@ -143,6 +143,7 @@ struct acpi_irq_parse_one_ctx {
|
|
unsigned int index;
|
|
unsigned long *res_flags;
|
|
struct irq_fwspec *fwspec;
|
|
+ bool skip_producer_check;
|
|
};
|
|
|
|
/**
|
|
@@ -216,7 +217,8 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
|
|
return AE_CTRL_TERMINATE;
|
|
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
|
|
eirq = &ares->data.extended_irq;
|
|
- if (eirq->producer_consumer == ACPI_PRODUCER)
|
|
+ if (!ctx->skip_producer_check &&
|
|
+ eirq->producer_consumer == ACPI_PRODUCER)
|
|
return AE_OK;
|
|
if (ctx->index >= eirq->interrupt_count) {
|
|
ctx->index -= eirq->interrupt_count;
|
|
@@ -252,8 +254,19 @@ static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
|
|
static int acpi_irq_parse_one(acpi_handle handle, unsigned int index,
|
|
struct irq_fwspec *fwspec, unsigned long *flags)
|
|
{
|
|
- struct acpi_irq_parse_one_ctx ctx = { -EINVAL, index, flags, fwspec };
|
|
+ struct acpi_irq_parse_one_ctx ctx = { -EINVAL, index, flags, fwspec, false };
|
|
|
|
+ /*
|
|
+ * Firmware on arm64-based HPE m400 platform incorrectly marks
|
|
+ * its UART interrupt as ACPI_PRODUCER rather than ACPI_CONSUMER.
|
|
+ * Don't do the producer/consumer check for that device.
|
|
+ */
|
|
+ if (IS_ENABLED(CONFIG_ARM64)) {
|
|
+ struct acpi_device *adev = acpi_get_acpi_dev(handle);
|
|
+
|
|
+ if (adev && !strcmp(acpi_device_hid(adev), "APMC0D08"))
|
|
+ ctx.skip_producer_check = true;
|
|
+ }
|
|
acpi_walk_resources(handle, METHOD_NAME__CRS, acpi_irq_parse_one_cb, &ctx);
|
|
return ctx.rc;
|
|
}
|
|
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
|
|
index f2e032f381625..295e429f0d8e0 100644
|
|
--- a/drivers/acpi/scan.c
|
|
+++ b/drivers/acpi/scan.c
|
|
@@ -1798,6 +1798,15 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
|
|
if (!acpi_match_device_ids(device, ignore_serial_bus_ids))
|
|
return false;
|
|
|
|
+ /*
|
|
+ * Firmware on some arm64 X-Gene platforms will make the UART
|
|
+ * device appear as both a UART and a slave of that UART. Just
|
|
+ * bail out here for X-Gene UARTs.
|
|
+ */
|
|
+ if (IS_ENABLED(CONFIG_ARM64) &&
|
|
+ !strcmp(acpi_device_hid(device), "APMC0D08"))
|
|
+ return false;
|
|
+
|
|
INIT_LIST_HEAD(&resource_list);
|
|
acpi_dev_get_resources(device, &resource_list,
|
|
acpi_check_serial_bus_slave,
|
|
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
|
|
index c79abdfcd7a9b..e23bfb7f94c72 100644
|
|
--- a/drivers/ata/libahci.c
|
|
+++ b/drivers/ata/libahci.c
|
|
@@ -731,6 +731,24 @@ int ahci_stop_engine(struct ata_port *ap)
|
|
tmp &= ~PORT_CMD_START;
|
|
writel(tmp, port_mmio + PORT_CMD);
|
|
|
|
+#ifdef CONFIG_ARM64
|
|
+ /* Rev Ax of Cavium CN99XX needs a hack for port stop */
|
|
+ if (dev_is_pci(ap->host->dev) &&
|
|
+ to_pci_dev(ap->host->dev)->vendor == 0x14e4 &&
|
|
+ to_pci_dev(ap->host->dev)->device == 0x9027 &&
|
|
+ midr_is_cpu_model_range(read_cpuid_id(),
|
|
+ MIDR_CPU_MODEL(ARM_CPU_IMP_BRCM, BRCM_CPU_PART_VULCAN),
|
|
+ MIDR_CPU_VAR_REV(0, 0),
|
|
+ MIDR_CPU_VAR_REV(0, MIDR_REVISION_MASK))) {
|
|
+ tmp = readl(hpriv->mmio + 0x8000);
|
|
+ udelay(100);
|
|
+ writel(tmp | (1 << 26), hpriv->mmio + 0x8000);
|
|
+ udelay(100);
|
|
+ writel(tmp & ~(1 << 26), hpriv->mmio + 0x8000);
|
|
+ dev_warn(ap->host->dev, "CN99XX SATA reset workaround applied\n");
|
|
+ }
|
|
+#endif
|
|
+
|
|
/* wait for engine to stop. This could be as long as 500 msec */
|
|
tmp = ata_wait_register(ap, port_mmio + PORT_CMD,
|
|
PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500);
|
|
diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c
|
|
index bbf7029e224be..cf7faa970dd65 100644
|
|
--- a/drivers/char/ipmi/ipmi_dmi.c
|
|
+++ b/drivers/char/ipmi/ipmi_dmi.c
|
|
@@ -215,6 +215,21 @@ static int __init scan_for_dmi_ipmi(void)
|
|
{
|
|
const struct dmi_device *dev = NULL;
|
|
|
|
+#ifdef CONFIG_ARM64
|
|
+ /* RHEL-only
|
|
+ * If this is ARM-based HPE m400, return now, because that platform
|
|
+ * reports the host-side ipmi address as intel port-io space, which
|
|
+ * does not exist in the ARM architecture.
|
|
+ */
|
|
+ const char *dmistr = dmi_get_system_info(DMI_PRODUCT_NAME);
|
|
+
|
|
+ if (dmistr && (strcmp("ProLiant m400 Server", dmistr) == 0)) {
|
|
+ pr_debug("%s does not support host ipmi\n", dmistr);
|
|
+ return 0;
|
|
+ }
|
|
+ /* END RHEL-only */
|
|
+#endif
|
|
+
|
|
while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev)))
|
|
dmi_decode_ipmi((const struct dmi_header *) dev->device_data);
|
|
|
|
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
|
|
index fa52414eccdaa..a44565c888375 100644
|
|
--- a/drivers/char/ipmi/ipmi_msghandler.c
|
|
+++ b/drivers/char/ipmi/ipmi_msghandler.c
|
|
@@ -34,6 +34,7 @@
|
|
#include <linux/uuid.h>
|
|
#include <linux/nospec.h>
|
|
#include <linux/vmalloc.h>
|
|
+#include <linux/dmi.h>
|
|
#include <linux/delay.h>
|
|
|
|
#define IPMI_DRIVER_VERSION "39.2"
|
|
@@ -5520,8 +5521,21 @@ static int __init ipmi_init_msghandler_mod(void)
|
|
{
|
|
int rv;
|
|
|
|
- pr_info("version " IPMI_DRIVER_VERSION "\n");
|
|
+#ifdef CONFIG_ARM64
|
|
+ /* RHEL-only
|
|
+ * If this is ARM-based HPE m400, return now, because that platform
|
|
+ * reports the host-side ipmi address as intel port-io space, which
|
|
+ * does not exist in the ARM architecture.
|
|
+ */
|
|
+ const char *dmistr = dmi_get_system_info(DMI_PRODUCT_NAME);
|
|
|
|
+ if (dmistr && (strcmp("ProLiant m400 Server", dmistr) == 0)) {
|
|
+ pr_debug("%s does not support host ipmi\n", dmistr);
|
|
+ return -ENOSYS;
|
|
+ }
|
|
+ /* END RHEL-only */
|
|
+#endif
|
|
+ pr_info("version " IPMI_DRIVER_VERSION "\n");
|
|
mutex_lock(&ipmi_interfaces_mutex);
|
|
rv = ipmi_register_driver();
|
|
mutex_unlock(&ipmi_interfaces_mutex);
|
|
diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile
|
|
index 8efbcf699e4ff..96d5a1ca981df 100644
|
|
--- a/drivers/firmware/efi/Makefile
|
|
+++ b/drivers/firmware/efi/Makefile
|
|
@@ -25,6 +25,7 @@ subdir-$(CONFIG_EFI_STUB) += libstub
|
|
obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o
|
|
obj-$(CONFIG_EFI_TEST) += test/
|
|
obj-$(CONFIG_EFI_DEV_PATH_PARSER) += dev-path-parser.o
|
|
+obj-$(CONFIG_EFI) += secureboot.o
|
|
obj-$(CONFIG_APPLE_PROPERTIES) += apple-properties.o
|
|
obj-$(CONFIG_EFI_RCI2_TABLE) += rci2-table.o
|
|
obj-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += embedded-firmware.o
|
|
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
|
|
index 1ce428e2ac8a0..12a79ddc25432 100644
|
|
--- a/drivers/firmware/efi/efi.c
|
|
+++ b/drivers/firmware/efi/efi.c
|
|
@@ -33,6 +33,7 @@
|
|
#include <linux/memblock.h>
|
|
#include <linux/security.h>
|
|
#include <linux/notifier.h>
|
|
+#include <linux/bsearch.h>
|
|
|
|
#include <asm/early_ioremap.h>
|
|
|
|
@@ -1016,40 +1017,101 @@ int efi_mem_type(unsigned long phys_addr)
|
|
return -EINVAL;
|
|
}
|
|
|
|
+struct efi_error_code {
|
|
+ efi_status_t status;
|
|
+ int errno;
|
|
+ const char *description;
|
|
+};
|
|
+
|
|
+static const struct efi_error_code efi_error_codes[] = {
|
|
+ { EFI_SUCCESS, 0, "Success"},
|
|
+#if 0
|
|
+ { EFI_LOAD_ERROR, -EPICK_AN_ERRNO, "Load Error"},
|
|
+#endif
|
|
+ { EFI_INVALID_PARAMETER, -EINVAL, "Invalid Parameter"},
|
|
+ { EFI_UNSUPPORTED, -ENOSYS, "Unsupported"},
|
|
+ { EFI_BAD_BUFFER_SIZE, -ENOSPC, "Bad Buffer Size"},
|
|
+ { EFI_BUFFER_TOO_SMALL, -ENOSPC, "Buffer Too Small"},
|
|
+ { EFI_NOT_READY, -EAGAIN, "Not Ready"},
|
|
+ { EFI_DEVICE_ERROR, -EIO, "Device Error"},
|
|
+ { EFI_WRITE_PROTECTED, -EROFS, "Write Protected"},
|
|
+ { EFI_OUT_OF_RESOURCES, -ENOMEM, "Out of Resources"},
|
|
+#if 0
|
|
+ { EFI_VOLUME_CORRUPTED, -EPICK_AN_ERRNO, "Volume Corrupt"},
|
|
+ { EFI_VOLUME_FULL, -EPICK_AN_ERRNO, "Volume Full"},
|
|
+ { EFI_NO_MEDIA, -EPICK_AN_ERRNO, "No Media"},
|
|
+ { EFI_MEDIA_CHANGED, -EPICK_AN_ERRNO, "Media changed"},
|
|
+#endif
|
|
+ { EFI_NOT_FOUND, -ENOENT, "Not Found"},
|
|
+#if 0
|
|
+ { EFI_ACCESS_DENIED, -EPICK_AN_ERRNO, "Access Denied"},
|
|
+ { EFI_NO_RESPONSE, -EPICK_AN_ERRNO, "No Response"},
|
|
+ { EFI_NO_MAPPING, -EPICK_AN_ERRNO, "No mapping"},
|
|
+ { EFI_TIMEOUT, -EPICK_AN_ERRNO, "Time out"},
|
|
+ { EFI_NOT_STARTED, -EPICK_AN_ERRNO, "Not started"},
|
|
+ { EFI_ALREADY_STARTED, -EPICK_AN_ERRNO, "Already started"},
|
|
+#endif
|
|
+ { EFI_ABORTED, -EINTR, "Aborted"},
|
|
+#if 0
|
|
+ { EFI_ICMP_ERROR, -EPICK_AN_ERRNO, "ICMP Error"},
|
|
+ { EFI_TFTP_ERROR, -EPICK_AN_ERRNO, "TFTP Error"},
|
|
+ { EFI_PROTOCOL_ERROR, -EPICK_AN_ERRNO, "Protocol Error"},
|
|
+ { EFI_INCOMPATIBLE_VERSION, -EPICK_AN_ERRNO, "Incompatible Version"},
|
|
+#endif
|
|
+ { EFI_SECURITY_VIOLATION, -EACCES, "Security Policy Violation"},
|
|
+#if 0
|
|
+ { EFI_CRC_ERROR, -EPICK_AN_ERRNO, "CRC Error"},
|
|
+ { EFI_END_OF_MEDIA, -EPICK_AN_ERRNO, "End of Media"},
|
|
+ { EFI_END_OF_FILE, -EPICK_AN_ERRNO, "End of File"},
|
|
+ { EFI_INVALID_LANGUAGE, -EPICK_AN_ERRNO, "Invalid Languages"},
|
|
+ { EFI_COMPROMISED_DATA, -EPICK_AN_ERRNO, "Compromised Data"},
|
|
+
|
|
+ // warnings
|
|
+ { EFI_WARN_UNKOWN_GLYPH, -EPICK_AN_ERRNO, "Warning Unknown Glyph"},
|
|
+ { EFI_WARN_DELETE_FAILURE, -EPICK_AN_ERRNO, "Warning Delete Failure"},
|
|
+ { EFI_WARN_WRITE_FAILURE, -EPICK_AN_ERRNO, "Warning Write Failure"},
|
|
+ { EFI_WARN_BUFFER_TOO_SMALL, -EPICK_AN_ERRNO, "Warning Buffer Too Small"},
|
|
+#endif
|
|
+};
|
|
+
|
|
+static int
|
|
+efi_status_cmp_bsearch(const void *key, const void *item)
|
|
+{
|
|
+ u64 status = (u64)(uintptr_t)key;
|
|
+ struct efi_error_code *code = (struct efi_error_code *)item;
|
|
+
|
|
+ if (status < code->status)
|
|
+ return -1;
|
|
+ if (status > code->status)
|
|
+ return 1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
int efi_status_to_err(efi_status_t status)
|
|
{
|
|
- int err;
|
|
-
|
|
- switch (status) {
|
|
- case EFI_SUCCESS:
|
|
- err = 0;
|
|
- break;
|
|
- case EFI_INVALID_PARAMETER:
|
|
- err = -EINVAL;
|
|
- break;
|
|
- case EFI_OUT_OF_RESOURCES:
|
|
- err = -ENOSPC;
|
|
- break;
|
|
- case EFI_DEVICE_ERROR:
|
|
- err = -EIO;
|
|
- break;
|
|
- case EFI_WRITE_PROTECTED:
|
|
- err = -EROFS;
|
|
- break;
|
|
- case EFI_SECURITY_VIOLATION:
|
|
- err = -EACCES;
|
|
- break;
|
|
- case EFI_NOT_FOUND:
|
|
- err = -ENOENT;
|
|
- break;
|
|
- case EFI_ABORTED:
|
|
- err = -EINTR;
|
|
- break;
|
|
- default:
|
|
- err = -EINVAL;
|
|
- }
|
|
+ struct efi_error_code *found;
|
|
+ size_t num = sizeof(efi_error_codes) / sizeof(struct efi_error_code);
|
|
|
|
- return err;
|
|
+ found = bsearch((void *)(uintptr_t)status, efi_error_codes,
|
|
+ sizeof(struct efi_error_code), num,
|
|
+ efi_status_cmp_bsearch);
|
|
+ if (!found)
|
|
+ return -EINVAL;
|
|
+ return found->errno;
|
|
+}
|
|
+
|
|
+const char *
|
|
+efi_status_to_str(efi_status_t status)
|
|
+{
|
|
+ struct efi_error_code *found;
|
|
+ size_t num = sizeof(efi_error_codes) / sizeof(struct efi_error_code);
|
|
+
|
|
+ found = bsearch((void *)(uintptr_t)status, efi_error_codes,
|
|
+ sizeof(struct efi_error_code), num,
|
|
+ efi_status_cmp_bsearch);
|
|
+ if (!found)
|
|
+ return "Unknown error code";
|
|
+ return found->description;
|
|
}
|
|
EXPORT_SYMBOL_GPL(efi_status_to_err);
|
|
|
|
diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c
|
|
index 6a337f1f8787b..89244e0d9fa86 100644
|
|
--- a/drivers/firmware/efi/libstub/fdt.c
|
|
+++ b/drivers/firmware/efi/libstub/fdt.c
|
|
@@ -132,6 +132,11 @@ static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size,
|
|
}
|
|
}
|
|
|
|
+ fdt_val32 = cpu_to_fdt32((u32)efi_get_secureboot());
|
|
+ status = fdt_setprop_var(fdt, node, "secure-boot-mode", fdt_val32);
|
|
+ if (status)
|
|
+ goto fdt_set_fail;
|
|
+
|
|
/* Shrink the FDT back to its minimum size: */
|
|
fdt_pack(fdt);
|
|
|
|
diff --git a/drivers/firmware/efi/libstub/secureboot.c b/drivers/firmware/efi/libstub/secureboot.c
|
|
index 516f4f0069bd2..380354755108b 100644
|
|
--- a/drivers/firmware/efi/libstub/secureboot.c
|
|
+++ b/drivers/firmware/efi/libstub/secureboot.c
|
|
@@ -29,10 +29,13 @@ enum efi_secureboot_mode efi_get_secureboot(void)
|
|
{
|
|
u32 attr;
|
|
unsigned long size;
|
|
- enum efi_secureboot_mode mode;
|
|
+ static enum efi_secureboot_mode mode;
|
|
efi_status_t status;
|
|
u8 moksbstate;
|
|
|
|
+ if (mode != efi_secureboot_mode_unset)
|
|
+ return mode;
|
|
+
|
|
mode = efi_get_secureboot_mode(get_var);
|
|
if (mode == efi_secureboot_mode_unknown) {
|
|
efi_err("Could not determine UEFI Secure Boot status.\n");
|
|
@@ -53,10 +56,13 @@ enum efi_secureboot_mode efi_get_secureboot(void)
|
|
/* If it fails, we don't care why. Default to secure */
|
|
if (status != EFI_SUCCESS)
|
|
goto secure_boot_enabled;
|
|
- if (!(attr & EFI_VARIABLE_NON_VOLATILE) && moksbstate == 1)
|
|
- return efi_secureboot_mode_disabled;
|
|
+ if (!(attr & EFI_VARIABLE_NON_VOLATILE) && moksbstate == 1) {
|
|
+ mode = efi_secureboot_mode_disabled;
|
|
+ return mode;
|
|
+ }
|
|
|
|
secure_boot_enabled:
|
|
efi_info("UEFI Secure Boot is enabled.\n");
|
|
- return efi_secureboot_mode_enabled;
|
|
+ mode = efi_secureboot_mode_enabled;
|
|
+ return mode;
|
|
}
|
|
diff --git a/drivers/firmware/efi/secureboot.c b/drivers/firmware/efi/secureboot.c
|
|
new file mode 100644
|
|
index 0000000000000..de0a3714a5d44
|
|
--- /dev/null
|
|
+++ b/drivers/firmware/efi/secureboot.c
|
|
@@ -0,0 +1,38 @@
|
|
+/* Core kernel secure boot support.
|
|
+ *
|
|
+ * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
|
|
+ * Written by David Howells (dhowells@redhat.com)
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public Licence
|
|
+ * as published by the Free Software Foundation; either version
|
|
+ * 2 of the Licence, or (at your option) any later version.
|
|
+ */
|
|
+
|
|
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
+
|
|
+#include <linux/efi.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/printk.h>
|
|
+
|
|
+/*
|
|
+ * Decide what to do when UEFI secure boot mode is enabled.
|
|
+ */
|
|
+void __init efi_set_secure_boot(enum efi_secureboot_mode mode)
|
|
+{
|
|
+ if (efi_enabled(EFI_BOOT)) {
|
|
+ switch (mode) {
|
|
+ case efi_secureboot_mode_disabled:
|
|
+ pr_info("Secure boot disabled\n");
|
|
+ break;
|
|
+ case efi_secureboot_mode_enabled:
|
|
+ set_bit(EFI_SECURE_BOOT, &efi.flags);
|
|
+ pr_info("Secure boot enabled\n");
|
|
+ break;
|
|
+ default:
|
|
+ pr_warn("Secure boot could not be determined (mode %u)\n",
|
|
+ mode);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
|
|
index d8ac40d0eb6fb..efd72c72ad401 100644
|
|
--- a/drivers/gpio/Kconfig
|
|
+++ b/drivers/gpio/Kconfig
|
|
@@ -1923,6 +1923,17 @@ config GPIO_MPSSE
|
|
GPIO driver for FTDI's MPSSE interface. These can do input and
|
|
output. Each MPSSE provides 16 IO pins.
|
|
|
|
+config GPIO_USBIO
|
|
+ tristate "Intel USBIO GPIO support"
|
|
+ depends on USB_USBIO
|
|
+ default USB_USBIO
|
|
+ help
|
|
+ Select this option to enable GPIO driver for the INTEL
|
|
+ USBIO driver stack.
|
|
+
|
|
+ This driver can also be built as a module. If so, the module
|
|
+ will be called gpio_usbio.
|
|
+
|
|
endmenu
|
|
|
|
menu "Virtual GPIO drivers"
|
|
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
|
|
index 379f55e9ed1e6..b1593ce92ebe7 100644
|
|
--- a/drivers/gpio/Makefile
|
|
+++ b/drivers/gpio/Makefile
|
|
@@ -192,6 +192,7 @@ obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o
|
|
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
|
|
obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
|
|
obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.o
|
|
+obj-$(CONFIG_GPIO_USBIO) += gpio-usbio.o
|
|
obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o
|
|
obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o
|
|
obj-$(CONFIG_GPIO_VIRTUSER) += gpio-virtuser.o
|
|
diff --git a/drivers/gpio/gpio-usbio.c b/drivers/gpio/gpio-usbio.c
|
|
new file mode 100644
|
|
index 0000000000000..34d42c743d5bc
|
|
--- /dev/null
|
|
+++ b/drivers/gpio/gpio-usbio.c
|
|
@@ -0,0 +1,248 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * Copyright (c) 2025 Intel Corporation.
|
|
+ * Copyright (c) 2025 Red Hat, Inc.
|
|
+ */
|
|
+
|
|
+#include <linux/acpi.h>
|
|
+#include <linux/auxiliary_bus.h>
|
|
+#include <linux/cleanup.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/gpio/driver.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/usb/usbio.h>
|
|
+
|
|
+struct usbio_gpio_bank {
|
|
+ u8 config[USBIO_GPIOSPERBANK];
|
|
+ u32 bitmap;
|
|
+};
|
|
+
|
|
+struct usbio_gpio {
|
|
+ struct mutex config_mutex; /* Protects banks[x].config */
|
|
+ struct usbio_gpio_bank banks[USBIO_MAX_GPIOBANKS];
|
|
+ struct gpio_chip gc;
|
|
+ struct auxiliary_device *adev;
|
|
+};
|
|
+
|
|
+static const struct acpi_device_id usbio_gpio_acpi_hids[] = {
|
|
+ { "INTC1007" }, /* MTL */
|
|
+ { "INTC10B2" }, /* ARL */
|
|
+ { "INTC10B5" }, /* LNL */
|
|
+ { "INTC10D1" }, /* MTL-CVF */
|
|
+ { "INTC10E2" }, /* PTL */
|
|
+ { }
|
|
+};
|
|
+
|
|
+static void usbio_gpio_get_bank_and_pin(struct gpio_chip *gc, unsigned int offset,
|
|
+ struct usbio_gpio_bank **bank_ret,
|
|
+ unsigned int *pin_ret)
|
|
+{
|
|
+ struct usbio_gpio *gpio = gpiochip_get_data(gc);
|
|
+ struct device *dev = &gpio->adev->dev;
|
|
+ struct usbio_gpio_bank *bank;
|
|
+ unsigned int pin;
|
|
+
|
|
+ bank = &gpio->banks[offset / USBIO_GPIOSPERBANK];
|
|
+ pin = offset % USBIO_GPIOSPERBANK;
|
|
+ if (~bank->bitmap & BIT(pin)) {
|
|
+ /* The FW bitmap sometimes is invalid, warn and continue */
|
|
+ dev_warn_once(dev, FW_BUG "GPIO %u is not in FW pins bitmap\n", offset);
|
|
+ }
|
|
+
|
|
+ *bank_ret = bank;
|
|
+ *pin_ret = pin;
|
|
+}
|
|
+
|
|
+static int usbio_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
|
|
+{
|
|
+ struct usbio_gpio_bank *bank;
|
|
+ unsigned int pin;
|
|
+ u8 cfg;
|
|
+
|
|
+ usbio_gpio_get_bank_and_pin(gc, offset, &bank, &pin);
|
|
+
|
|
+ cfg = bank->config[pin] & USBIO_GPIO_PINMOD_MASK;
|
|
+
|
|
+ return (cfg == USBIO_GPIO_PINMOD_OUTPUT) ?
|
|
+ GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
|
|
+}
|
|
+
|
|
+static int usbio_gpio_get(struct gpio_chip *gc, unsigned int offset)
|
|
+{
|
|
+ struct usbio_gpio *gpio = gpiochip_get_data(gc);
|
|
+ struct usbio_gpio_bank *bank;
|
|
+ struct usbio_gpio_rw gbuf;
|
|
+ unsigned int pin;
|
|
+ int ret;
|
|
+
|
|
+ usbio_gpio_get_bank_and_pin(gc, offset, &bank, &pin);
|
|
+
|
|
+ gbuf.bankid = offset / USBIO_GPIOSPERBANK;
|
|
+ gbuf.pincount = 1;
|
|
+ gbuf.pin = pin;
|
|
+
|
|
+ ret = usbio_control_msg(gpio->adev, USBIO_PKTTYPE_GPIO, USBIO_GPIOCMD_READ,
|
|
+ &gbuf, sizeof(gbuf) - sizeof(gbuf.value),
|
|
+ &gbuf, sizeof(gbuf));
|
|
+ if (ret != sizeof(gbuf))
|
|
+ return (ret < 0) ? ret : -EPROTO;
|
|
+
|
|
+ return (le32_to_cpu(gbuf.value) >> pin) & 1;
|
|
+}
|
|
+
|
|
+static int usbio_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
|
|
+{
|
|
+ struct usbio_gpio *gpio = gpiochip_get_data(gc);
|
|
+ struct usbio_gpio_bank *bank;
|
|
+ struct usbio_gpio_rw gbuf;
|
|
+ unsigned int pin;
|
|
+
|
|
+ usbio_gpio_get_bank_and_pin(gc, offset, &bank, &pin);
|
|
+
|
|
+ gbuf.bankid = offset / USBIO_GPIOSPERBANK;
|
|
+ gbuf.pincount = 1;
|
|
+ gbuf.pin = pin;
|
|
+ gbuf.value = cpu_to_le32(value << pin);
|
|
+
|
|
+ return usbio_control_msg(gpio->adev, USBIO_PKTTYPE_GPIO, USBIO_GPIOCMD_WRITE,
|
|
+ &gbuf, sizeof(gbuf), NULL, 0);
|
|
+}
|
|
+
|
|
+static int usbio_gpio_update_config(struct gpio_chip *gc, unsigned int offset,
|
|
+ u8 mask, u8 value)
|
|
+{
|
|
+ struct usbio_gpio *gpio = gpiochip_get_data(gc);
|
|
+ struct usbio_gpio_bank *bank;
|
|
+ struct usbio_gpio_init gbuf;
|
|
+ unsigned int pin;
|
|
+
|
|
+ usbio_gpio_get_bank_and_pin(gc, offset, &bank, &pin);
|
|
+
|
|
+ guard(mutex)(&gpio->config_mutex);
|
|
+
|
|
+ bank->config[pin] &= ~mask;
|
|
+ bank->config[pin] |= value;
|
|
+
|
|
+ gbuf.bankid = offset / USBIO_GPIOSPERBANK;
|
|
+ gbuf.config = bank->config[pin];
|
|
+ gbuf.pincount = 1;
|
|
+ gbuf.pin = pin;
|
|
+
|
|
+ return usbio_control_msg(gpio->adev, USBIO_PKTTYPE_GPIO, USBIO_GPIOCMD_INIT,
|
|
+ &gbuf, sizeof(gbuf), NULL, 0);
|
|
+}
|
|
+
|
|
+static int usbio_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
|
|
+{
|
|
+ return usbio_gpio_update_config(gc, offset, USBIO_GPIO_PINMOD_MASK,
|
|
+ USBIO_GPIO_SET_PINMOD(USBIO_GPIO_PINMOD_INPUT));
|
|
+}
|
|
+
|
|
+static int usbio_gpio_direction_output(struct gpio_chip *gc,
|
|
+ unsigned int offset, int value)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = usbio_gpio_update_config(gc, offset, USBIO_GPIO_PINMOD_MASK,
|
|
+ USBIO_GPIO_SET_PINMOD(USBIO_GPIO_PINMOD_OUTPUT));
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return usbio_gpio_set(gc, offset, value);
|
|
+}
|
|
+
|
|
+static int usbio_gpio_set_config(struct gpio_chip *gc, unsigned int offset,
|
|
+ unsigned long config)
|
|
+{
|
|
+ u8 value;
|
|
+
|
|
+ switch (pinconf_to_config_param(config)) {
|
|
+ case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
|
|
+ value = USBIO_GPIO_SET_PINCFG(USBIO_GPIO_PINCFG_DEFAULT);
|
|
+ break;
|
|
+ case PIN_CONFIG_BIAS_PULL_UP:
|
|
+ value = USBIO_GPIO_SET_PINCFG(USBIO_GPIO_PINCFG_PULLUP);
|
|
+ break;
|
|
+ case PIN_CONFIG_BIAS_PULL_DOWN:
|
|
+ value = USBIO_GPIO_SET_PINCFG(USBIO_GPIO_PINCFG_PULLDOWN);
|
|
+ break;
|
|
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
|
|
+ value = USBIO_GPIO_SET_PINCFG(USBIO_GPIO_PINCFG_PUSHPULL);
|
|
+ break;
|
|
+ default:
|
|
+ return -ENOTSUPP;
|
|
+ }
|
|
+
|
|
+ return usbio_gpio_update_config(gc, offset, USBIO_GPIO_PINCFG_MASK, value);
|
|
+}
|
|
+
|
|
+static int usbio_gpio_probe(struct auxiliary_device *adev,
|
|
+ const struct auxiliary_device_id *adev_id)
|
|
+{
|
|
+ struct usbio_gpio_bank_desc *bank_desc;
|
|
+ struct device *dev = &adev->dev;
|
|
+ struct usbio_gpio *gpio;
|
|
+ int bank, ret;
|
|
+
|
|
+ bank_desc = dev_get_platdata(dev);
|
|
+ if (!bank_desc)
|
|
+ return -EINVAL;
|
|
+
|
|
+ gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL);
|
|
+ if (!gpio)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ret = devm_mutex_init(dev, &gpio->config_mutex);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ gpio->adev = adev;
|
|
+
|
|
+ usbio_acpi_bind(gpio->adev, usbio_gpio_acpi_hids);
|
|
+
|
|
+ for (bank = 0; bank < USBIO_MAX_GPIOBANKS && bank_desc[bank].bmap; bank++)
|
|
+ gpio->banks[bank].bitmap = le32_to_cpu(bank_desc[bank].bmap);
|
|
+
|
|
+ gpio->gc.label = ACPI_COMPANION(dev) ?
|
|
+ acpi_dev_name(ACPI_COMPANION(dev)) : dev_name(dev);
|
|
+ gpio->gc.parent = dev;
|
|
+ gpio->gc.owner = THIS_MODULE;
|
|
+ gpio->gc.get_direction = usbio_gpio_get_direction;
|
|
+ gpio->gc.direction_input = usbio_gpio_direction_input;
|
|
+ gpio->gc.direction_output = usbio_gpio_direction_output;
|
|
+ gpio->gc.get = usbio_gpio_get;
|
|
+ gpio->gc.set = usbio_gpio_set;
|
|
+ gpio->gc.set_config = usbio_gpio_set_config;
|
|
+ gpio->gc.base = -1;
|
|
+ gpio->gc.ngpio = bank * USBIO_GPIOSPERBANK;
|
|
+ gpio->gc.can_sleep = true;
|
|
+
|
|
+ ret = devm_gpiochip_add_data(dev, &gpio->gc, gpio);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (has_acpi_companion(dev))
|
|
+ acpi_dev_clear_dependencies(ACPI_COMPANION(dev));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct auxiliary_device_id usbio_gpio_id_table[] = {
|
|
+ { "usbio.usbio-gpio" },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(auxiliary, usbio_gpio_id_table);
|
|
+
|
|
+static struct auxiliary_driver usbio_gpio_driver = {
|
|
+ .name = USBIO_GPIO_CLIENT,
|
|
+ .probe = usbio_gpio_probe,
|
|
+ .id_table = usbio_gpio_id_table
|
|
+};
|
|
+module_auxiliary_driver(usbio_gpio_driver);
|
|
+
|
|
+MODULE_DESCRIPTION("Intel USBIO GPIO driver");
|
|
+MODULE_AUTHOR("Israel Cepeda <israel.a.cepeda.lopez@intel.com>");
|
|
+MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_IMPORT_NS("USBIO");
|
|
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
|
|
index d4af17fdba467..154f0403cbf4c 100644
|
|
--- a/drivers/hid/hid-rmi.c
|
|
+++ b/drivers/hid/hid-rmi.c
|
|
@@ -321,21 +321,12 @@ static int rmi_input_event(struct hid_device *hdev, u8 *data, int size)
|
|
{
|
|
struct rmi_data *hdata = hid_get_drvdata(hdev);
|
|
struct rmi_device *rmi_dev = hdata->xport.rmi_dev;
|
|
- unsigned long flags;
|
|
|
|
if (!(test_bit(RMI_STARTED, &hdata->flags)))
|
|
return 0;
|
|
|
|
- pm_wakeup_event(hdev->dev.parent, 0);
|
|
-
|
|
- local_irq_save(flags);
|
|
-
|
|
rmi_set_attn_data(rmi_dev, data[1], &data[2], size - 2);
|
|
|
|
- generic_handle_irq(hdata->rmi_irq);
|
|
-
|
|
- local_irq_restore(flags);
|
|
-
|
|
return 1;
|
|
}
|
|
|
|
@@ -589,56 +580,6 @@ static const struct rmi_transport_ops hid_rmi_ops = {
|
|
.reset = rmi_hid_reset,
|
|
};
|
|
|
|
-static void rmi_irq_teardown(void *data)
|
|
-{
|
|
- struct rmi_data *hdata = data;
|
|
- struct irq_domain *domain = hdata->domain;
|
|
-
|
|
- if (!domain)
|
|
- return;
|
|
-
|
|
- irq_dispose_mapping(irq_find_mapping(domain, 0));
|
|
-
|
|
- irq_domain_remove(domain);
|
|
- hdata->domain = NULL;
|
|
- hdata->rmi_irq = 0;
|
|
-}
|
|
-
|
|
-static int rmi_irq_map(struct irq_domain *h, unsigned int virq,
|
|
- irq_hw_number_t hw_irq_num)
|
|
-{
|
|
- irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq);
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static const struct irq_domain_ops rmi_irq_ops = {
|
|
- .map = rmi_irq_map,
|
|
-};
|
|
-
|
|
-static int rmi_setup_irq_domain(struct hid_device *hdev)
|
|
-{
|
|
- struct rmi_data *hdata = hid_get_drvdata(hdev);
|
|
- int ret;
|
|
-
|
|
- hdata->domain = irq_domain_create_linear(hdev->dev.fwnode, 1,
|
|
- &rmi_irq_ops, hdata);
|
|
- if (!hdata->domain)
|
|
- return -ENOMEM;
|
|
-
|
|
- ret = devm_add_action_or_reset(&hdev->dev, &rmi_irq_teardown, hdata);
|
|
- if (ret)
|
|
- return ret;
|
|
-
|
|
- hdata->rmi_irq = irq_create_mapping(hdata->domain, 0);
|
|
- if (hdata->rmi_irq <= 0) {
|
|
- hid_err(hdev, "Can't allocate an IRQ\n");
|
|
- return hdata->rmi_irq < 0 ? hdata->rmi_irq : -ENXIO;
|
|
- }
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
{
|
|
struct rmi_data *data = NULL;
|
|
@@ -711,18 +652,11 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
|
|
mutex_init(&data->page_mutex);
|
|
|
|
- ret = rmi_setup_irq_domain(hdev);
|
|
- if (ret) {
|
|
- hid_err(hdev, "failed to allocate IRQ domain\n");
|
|
- return ret;
|
|
- }
|
|
-
|
|
if (data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS)
|
|
rmi_hid_pdata.gpio_data.disable = true;
|
|
|
|
data->xport.dev = hdev->dev.parent;
|
|
data->xport.pdata = rmi_hid_pdata;
|
|
- data->xport.pdata.irq = data->rmi_irq;
|
|
data->xport.proto_name = "hid";
|
|
data->xport.ops = &hid_rmi_ops;
|
|
|
|
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
|
|
index 4b98a7bf4cb73..441e6fa3bed2a 100644
|
|
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
|
|
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
|
|
@@ -12,6 +12,7 @@
|
|
#include <linux/init.h>
|
|
#include <linux/types.h>
|
|
#include <linux/device.h>
|
|
+#include <linux/dmi.h>
|
|
#include <linux/io.h>
|
|
#include <linux/err.h>
|
|
#include <linux/fs.h>
|
|
@@ -2451,6 +2452,16 @@ static const struct amba_id etm4_ids[] = {
|
|
{},
|
|
};
|
|
|
|
+static const struct dmi_system_id broken_coresight[] = {
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_MATCH(DMI_SYS_VENDOR, "HPE"),
|
|
+ DMI_MATCH(DMI_PRODUCT_NAME, "Apollo 70"),
|
|
+ },
|
|
+ },
|
|
+ { } /* terminating entry */
|
|
+};
|
|
+
|
|
MODULE_DEVICE_TABLE(amba, etm4_ids);
|
|
|
|
static struct amba_driver etm4x_amba_driver = {
|
|
@@ -2525,6 +2536,11 @@ static int __init etm4x_init(void)
|
|
{
|
|
int ret;
|
|
|
|
+ if (dmi_check_system(broken_coresight)) {
|
|
+ pr_info("ETM4 disabled due to firmware bug\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
ret = etm4_pm_setup();
|
|
|
|
/* etm4_pm_setup() does its own cleanup - exit on error */
|
|
@@ -2551,6 +2567,9 @@ static int __init etm4x_init(void)
|
|
|
|
static void __exit etm4x_exit(void)
|
|
{
|
|
+ if (dmi_check_system(broken_coresight))
|
|
+ return;
|
|
+
|
|
amba_driver_unregister(&etm4x_amba_driver);
|
|
platform_driver_unregister(&etm4_platform_driver);
|
|
etm4_pm_clear();
|
|
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
|
|
index 070d014fdc5d5..06b1b702fd7a7 100644
|
|
--- a/drivers/i2c/busses/Kconfig
|
|
+++ b/drivers/i2c/busses/Kconfig
|
|
@@ -1357,6 +1357,17 @@ config I2C_LJCA
|
|
This driver can also be built as a module. If so, the module
|
|
will be called i2c-ljca.
|
|
|
|
+config I2C_USBIO
|
|
+ tristate "Intel USBIO I2C Adapter support"
|
|
+ depends on USB_USBIO
|
|
+ default USB_USBIO
|
|
+ help
|
|
+ Select this option to enable I2C driver for the INTEL
|
|
+ USBIO driver stack.
|
|
+
|
|
+ This driver can also be built as a module. If so, the module
|
|
+ will be called i2c_usbio.
|
|
+
|
|
config I2C_CP2615
|
|
tristate "Silicon Labs CP2615 USB sound card and I2C adapter"
|
|
depends on USB
|
|
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
|
|
index 04db855fdfd66..401a79c9767e6 100644
|
|
--- a/drivers/i2c/busses/Makefile
|
|
+++ b/drivers/i2c/busses/Makefile
|
|
@@ -135,6 +135,7 @@ obj-$(CONFIG_I2C_GXP) += i2c-gxp.o
|
|
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
|
|
obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o
|
|
obj-$(CONFIG_I2C_LJCA) += i2c-ljca.o
|
|
+obj-$(CONFIG_I2C_USBIO) += i2c-usbio.o
|
|
obj-$(CONFIG_I2C_CP2615) += i2c-cp2615.o
|
|
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
|
|
obj-$(CONFIG_I2C_PCI1XXXX) += i2c-mchp-pci1xxxx.o
|
|
diff --git a/drivers/i2c/busses/i2c-usbio.c b/drivers/i2c/busses/i2c-usbio.c
|
|
new file mode 100644
|
|
index 0000000000000..e7799abf67877
|
|
--- /dev/null
|
|
+++ b/drivers/i2c/busses/i2c-usbio.c
|
|
@@ -0,0 +1,321 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * Copyright (c) 2025 Intel Corporation.
|
|
+ * Copyright (c) 2025 Red Hat, Inc.
|
|
+ */
|
|
+
|
|
+#include <linux/auxiliary_bus.h>
|
|
+#include <linux/dev_printk.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/usb/usbio.h>
|
|
+
|
|
+#define I2C_RW_OVERHEAD (sizeof(struct usbio_bulk_packet) + sizeof(struct usbio_i2c_rw))
|
|
+
|
|
+struct usbio_i2c {
|
|
+ struct i2c_adapter adap;
|
|
+ struct auxiliary_device *adev;
|
|
+ struct usbio_i2c_rw *rwbuf;
|
|
+ unsigned long quirks;
|
|
+ u32 speed;
|
|
+ u16 txbuf_len;
|
|
+ u16 rxbuf_len;
|
|
+};
|
|
+
|
|
+static const struct acpi_device_id usbio_i2c_acpi_hids[] = {
|
|
+ { "INTC1008" }, /* MTL */
|
|
+ { "INTC10B3" }, /* ARL */
|
|
+ { "INTC10B6" }, /* LNL */
|
|
+ { "INTC10D2" }, /* MTL-CVF */
|
|
+ { "INTC10E3" }, /* PTL */
|
|
+ { }
|
|
+};
|
|
+
|
|
+static const u32 usbio_i2c_speeds[] = {
|
|
+ I2C_MAX_STANDARD_MODE_FREQ,
|
|
+ I2C_MAX_FAST_MODE_FREQ,
|
|
+ I2C_MAX_FAST_MODE_PLUS_FREQ,
|
|
+ I2C_MAX_HIGH_SPEED_MODE_FREQ
|
|
+};
|
|
+
|
|
+static void usbio_i2c_uninit(struct i2c_adapter *adap, struct i2c_msg *msg)
|
|
+{
|
|
+ struct usbio_i2c *i2c = i2c_get_adapdata(adap);
|
|
+ struct usbio_i2c_uninit ubuf;
|
|
+
|
|
+ ubuf.busid = i2c->adev->id;
|
|
+ ubuf.config = cpu_to_le16(msg->addr);
|
|
+
|
|
+ usbio_bulk_msg(i2c->adev, USBIO_PKTTYPE_I2C, USBIO_I2CCMD_UNINIT, true,
|
|
+ &ubuf, sizeof(ubuf), NULL, 0);
|
|
+}
|
|
+
|
|
+static int usbio_i2c_init(struct i2c_adapter *adap, struct i2c_msg *msg)
|
|
+{
|
|
+ struct usbio_i2c *i2c = i2c_get_adapdata(adap);
|
|
+ struct usbio_i2c_init ibuf;
|
|
+ void *reply_buf;
|
|
+ u16 reply_len;
|
|
+ int ret;
|
|
+
|
|
+ ibuf.busid = i2c->adev->id;
|
|
+ ibuf.config = cpu_to_le16(msg->addr);
|
|
+ ibuf.speed = cpu_to_le32(i2c->speed);
|
|
+
|
|
+ if (i2c->quirks & USBIO_QUIRK_I2C_NO_INIT_ACK) {
|
|
+ reply_buf = NULL;
|
|
+ reply_len = 0;
|
|
+ } else {
|
|
+ reply_buf = &ibuf;
|
|
+ reply_len = sizeof(ibuf);
|
|
+ }
|
|
+
|
|
+ ret = usbio_bulk_msg(i2c->adev, USBIO_PKTTYPE_I2C, USBIO_I2CCMD_INIT, true,
|
|
+ &ibuf, sizeof(ibuf), reply_buf, reply_len);
|
|
+ if (ret != sizeof(ibuf))
|
|
+ return (ret < 0) ? ret : -EIO;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int usbio_i2c_read(struct i2c_adapter *adap, struct i2c_msg *msg)
|
|
+{
|
|
+ struct usbio_i2c *i2c = i2c_get_adapdata(adap);
|
|
+ u16 rxchunk = i2c->rxbuf_len - I2C_RW_OVERHEAD;
|
|
+ struct usbio_i2c_rw *rbuf = i2c->rwbuf;
|
|
+ int ret;
|
|
+
|
|
+ rbuf->busid = i2c->adev->id;
|
|
+ rbuf->config = cpu_to_le16(msg->addr);
|
|
+ rbuf->size = cpu_to_le16(msg->len);
|
|
+
|
|
+ if (msg->len > rxchunk) {
|
|
+ /* Need to split the input buffer */
|
|
+ u16 len = 0;
|
|
+
|
|
+ do {
|
|
+ if (msg->len - len < rxchunk)
|
|
+ rxchunk = msg->len - len;
|
|
+
|
|
+ ret = usbio_bulk_msg(i2c->adev, USBIO_PKTTYPE_I2C,
|
|
+ USBIO_I2CCMD_READ, true,
|
|
+ rbuf, len == 0 ? sizeof(*rbuf) : 0,
|
|
+ rbuf, sizeof(*rbuf) + rxchunk);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ memcpy(&msg->buf[len], rbuf->data, rxchunk);
|
|
+ len += rxchunk;
|
|
+ } while (msg->len > len);
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ ret = usbio_bulk_msg(i2c->adev, USBIO_PKTTYPE_I2C, USBIO_I2CCMD_READ, true,
|
|
+ rbuf, sizeof(*rbuf), rbuf, sizeof(*rbuf) + msg->len);
|
|
+ if (ret != sizeof(*rbuf) + msg->len)
|
|
+ return (ret < 0) ? ret : -EIO;
|
|
+
|
|
+ memcpy(msg->buf, rbuf->data, msg->len);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int usbio_i2c_write(struct i2c_adapter *adap, struct i2c_msg *msg)
|
|
+{
|
|
+ struct usbio_i2c *i2c = i2c_get_adapdata(adap);
|
|
+ u16 txchunk = i2c->txbuf_len - I2C_RW_OVERHEAD;
|
|
+ struct usbio_i2c_rw *wbuf = i2c->rwbuf;
|
|
+ int ret;
|
|
+
|
|
+ if (msg->len > txchunk) {
|
|
+ /* Need to split the output buffer */
|
|
+ u16 len = 0;
|
|
+
|
|
+ do {
|
|
+ wbuf->busid = i2c->adev->id;
|
|
+ wbuf->config = cpu_to_le16(msg->addr);
|
|
+
|
|
+ if (i2c->quirks & USBIO_QUIRK_I2C_USE_CHUNK_LEN)
|
|
+ wbuf->size = cpu_to_le16(txchunk);
|
|
+ else
|
|
+ wbuf->size = cpu_to_le16(msg->len);
|
|
+
|
|
+ memcpy(wbuf->data, &msg->buf[len], txchunk);
|
|
+ len += txchunk;
|
|
+
|
|
+ ret = usbio_bulk_msg(i2c->adev, USBIO_PKTTYPE_I2C,
|
|
+ USBIO_I2CCMD_WRITE, msg->len == len,
|
|
+ wbuf, sizeof(*wbuf) + txchunk,
|
|
+ wbuf, sizeof(*wbuf));
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+
|
|
+ if (msg->len - len < txchunk)
|
|
+ txchunk = msg->len - len;
|
|
+ } while (msg->len > len);
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ wbuf->busid = i2c->adev->id;
|
|
+ wbuf->config = cpu_to_le16(msg->addr);
|
|
+ wbuf->size = cpu_to_le16(msg->len);
|
|
+ memcpy(wbuf->data, msg->buf, msg->len);
|
|
+
|
|
+ ret = usbio_bulk_msg(i2c->adev, USBIO_PKTTYPE_I2C, USBIO_I2CCMD_WRITE, true,
|
|
+ wbuf, sizeof(*wbuf) + msg->len, wbuf, sizeof(*wbuf));
|
|
+ if (ret != sizeof(*wbuf) || le16_to_cpu(wbuf->size) != msg->len)
|
|
+ return (ret < 0) ? ret : -EIO;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int usbio_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
|
+{
|
|
+ struct usbio_i2c *i2c = i2c_get_adapdata(adap);
|
|
+ int ret;
|
|
+
|
|
+ usbio_acquire(i2c->adev);
|
|
+
|
|
+ ret = usbio_i2c_init(adap, msgs);
|
|
+ if (ret)
|
|
+ goto out_release;
|
|
+
|
|
+ for (int i = 0; i < num; ret = ++i) {
|
|
+ if (msgs[i].flags & I2C_M_RD)
|
|
+ ret = usbio_i2c_read(adap, &msgs[i]);
|
|
+ else
|
|
+ ret = usbio_i2c_write(adap, &msgs[i]);
|
|
+
|
|
+ if (ret)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ usbio_i2c_uninit(adap, msgs);
|
|
+
|
|
+out_release:
|
|
+ usbio_release(i2c->adev);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static u32 usbio_i2c_func(struct i2c_adapter *adap)
|
|
+{
|
|
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
|
+}
|
|
+
|
|
+static const struct i2c_adapter_quirks usbio_i2c_quirks = {
|
|
+ .flags = I2C_AQ_NO_ZERO_LEN | I2C_AQ_NO_REP_START,
|
|
+ .max_read_len = SZ_4K,
|
|
+ .max_write_len = SZ_4K,
|
|
+};
|
|
+
|
|
+static const struct i2c_adapter_quirks usbio_i2c_quirks_max_rw_len52 = {
|
|
+ .flags = I2C_AQ_NO_ZERO_LEN | I2C_AQ_NO_REP_START,
|
|
+ .max_read_len = 52,
|
|
+ .max_write_len = 52,
|
|
+};
|
|
+
|
|
+static const struct i2c_algorithm usbio_i2c_algo = {
|
|
+ .master_xfer = usbio_i2c_xfer,
|
|
+ .functionality = usbio_i2c_func,
|
|
+};
|
|
+
|
|
+static int usbio_i2c_probe(struct auxiliary_device *adev,
|
|
+ const struct auxiliary_device_id *adev_id)
|
|
+{
|
|
+ struct usbio_i2c_bus_desc *i2c_desc;
|
|
+ struct device *dev = &adev->dev;
|
|
+ struct usbio_i2c *i2c;
|
|
+ u32 max_speed;
|
|
+ int ret;
|
|
+
|
|
+ i2c_desc = dev_get_platdata(dev);
|
|
+ if (!i2c_desc)
|
|
+ return -EINVAL;
|
|
+
|
|
+ i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
|
|
+ if (!i2c)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ i2c->adev = adev;
|
|
+
|
|
+ usbio_acpi_bind(i2c->adev, usbio_i2c_acpi_hids);
|
|
+ usbio_get_txrxbuf_len(i2c->adev, &i2c->txbuf_len, &i2c->rxbuf_len);
|
|
+
|
|
+ i2c->rwbuf = devm_kzalloc(dev, max(i2c->txbuf_len, i2c->rxbuf_len), GFP_KERNEL);
|
|
+ if (!i2c->rwbuf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ i2c->quirks = usbio_get_quirks(i2c->adev);
|
|
+
|
|
+ max_speed = usbio_i2c_speeds[i2c_desc->caps & USBIO_I2C_BUS_MODE_CAP_MASK];
|
|
+ if (max_speed < I2C_MAX_FAST_MODE_FREQ &&
|
|
+ (i2c->quirks & USBIO_QUIRK_I2C_ALLOW_400KHZ))
|
|
+ max_speed = I2C_MAX_FAST_MODE_FREQ;
|
|
+
|
|
+ i2c->speed = i2c_acpi_find_bus_speed(dev);
|
|
+ if (!i2c->speed)
|
|
+ i2c->speed = I2C_MAX_STANDARD_MODE_FREQ;
|
|
+ else if (i2c->speed > max_speed) {
|
|
+ dev_warn(dev, "Invalid speed %u adjusting to bus max %u\n",
|
|
+ i2c->speed, max_speed);
|
|
+ i2c->speed = max_speed;
|
|
+ }
|
|
+
|
|
+ i2c->adap.owner = THIS_MODULE;
|
|
+ i2c->adap.class = I2C_CLASS_HWMON;
|
|
+ i2c->adap.dev.parent = dev;
|
|
+ i2c->adap.algo = &usbio_i2c_algo;
|
|
+
|
|
+ if (i2c->quirks & USBIO_QUIRK_I2C_MAX_RW_LEN_52)
|
|
+ i2c->adap.quirks = &usbio_i2c_quirks_max_rw_len52;
|
|
+ else
|
|
+ i2c->adap.quirks = &usbio_i2c_quirks;
|
|
+
|
|
+ snprintf(i2c->adap.name, sizeof(i2c->adap.name), "%s.%d",
|
|
+ USBIO_I2C_CLIENT, i2c->adev->id);
|
|
+
|
|
+ device_set_node(&i2c->adap.dev, dev_fwnode(&adev->dev));
|
|
+
|
|
+ auxiliary_set_drvdata(adev, i2c);
|
|
+ i2c_set_adapdata(&i2c->adap, i2c);
|
|
+
|
|
+ ret = i2c_add_adapter(&i2c->adap);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (has_acpi_companion(&i2c->adap.dev))
|
|
+ acpi_dev_clear_dependencies(ACPI_COMPANION(&i2c->adap.dev));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void usbio_i2c_remove(struct auxiliary_device *adev)
|
|
+{
|
|
+ struct usbio_i2c *i2c = auxiliary_get_drvdata(adev);
|
|
+
|
|
+ i2c_del_adapter(&i2c->adap);
|
|
+}
|
|
+
|
|
+static const struct auxiliary_device_id usbio_i2c_id_table[] = {
|
|
+ { "usbio.usbio-i2c" },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(auxiliary, usbio_i2c_id_table);
|
|
+
|
|
+static struct auxiliary_driver usbio_i2c_driver = {
|
|
+ .name = USBIO_I2C_CLIENT,
|
|
+ .probe = usbio_i2c_probe,
|
|
+ .remove = usbio_i2c_remove,
|
|
+ .id_table = usbio_i2c_id_table
|
|
+};
|
|
+module_auxiliary_driver(usbio_i2c_driver);
|
|
+
|
|
+MODULE_DESCRIPTION("Intel USBIO I2C driver");
|
|
+MODULE_AUTHOR("Israel Cepeda <israel.a.cepeda.lopez@intel.com>");
|
|
+MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
|
|
+MODULE_LICENSE("GPL");
|
|
+MODULE_IMPORT_NS("USBIO");
|
|
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
|
|
index 2168b6cd71673..5d7cda175a0ce 100644
|
|
--- a/drivers/input/rmi4/rmi_driver.c
|
|
+++ b/drivers/input/rmi4/rmi_driver.c
|
|
@@ -182,34 +182,47 @@ void rmi_set_attn_data(struct rmi_device *rmi_dev, unsigned long irq_status,
|
|
attn_data.data = fifo_data;
|
|
|
|
kfifo_put(&drvdata->attn_fifo, attn_data);
|
|
+
|
|
+ schedule_work(&drvdata->attn_work);
|
|
}
|
|
EXPORT_SYMBOL_GPL(rmi_set_attn_data);
|
|
|
|
-static irqreturn_t rmi_irq_fn(int irq, void *dev_id)
|
|
+static void attn_callback(struct work_struct *work)
|
|
{
|
|
- struct rmi_device *rmi_dev = dev_id;
|
|
- struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev);
|
|
+ struct rmi_driver_data *drvdata = container_of(work,
|
|
+ struct rmi_driver_data,
|
|
+ attn_work);
|
|
struct rmi4_attn_data attn_data = {0};
|
|
int ret, count;
|
|
|
|
count = kfifo_get(&drvdata->attn_fifo, &attn_data);
|
|
- if (count) {
|
|
- *(drvdata->irq_status) = attn_data.irq_status;
|
|
- drvdata->attn_data = attn_data;
|
|
- }
|
|
+ if (!count)
|
|
+ return;
|
|
|
|
- ret = rmi_process_interrupt_requests(rmi_dev);
|
|
+ *(drvdata->irq_status) = attn_data.irq_status;
|
|
+ drvdata->attn_data = attn_data;
|
|
+
|
|
+ ret = rmi_process_interrupt_requests(drvdata->rmi_dev);
|
|
if (ret)
|
|
- rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev,
|
|
+ rmi_dbg(RMI_DEBUG_CORE, &drvdata->rmi_dev->dev,
|
|
"Failed to process interrupt request: %d\n", ret);
|
|
|
|
- if (count) {
|
|
- kfree(attn_data.data);
|
|
- drvdata->attn_data.data = NULL;
|
|
- }
|
|
+ kfree(attn_data.data);
|
|
+ drvdata->attn_data.data = NULL;
|
|
|
|
if (!kfifo_is_empty(&drvdata->attn_fifo))
|
|
- return rmi_irq_fn(irq, dev_id);
|
|
+ schedule_work(&drvdata->attn_work);
|
|
+}
|
|
+
|
|
+static irqreturn_t rmi_irq_fn(int irq, void *dev_id)
|
|
+{
|
|
+ struct rmi_device *rmi_dev = dev_id;
|
|
+ int ret;
|
|
+
|
|
+ ret = rmi_process_interrupt_requests(rmi_dev);
|
|
+ if (ret)
|
|
+ rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev,
|
|
+ "Failed to process interrupt request: %d\n", ret);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
@@ -217,7 +230,6 @@ static irqreturn_t rmi_irq_fn(int irq, void *dev_id)
|
|
static int rmi_irq_init(struct rmi_device *rmi_dev)
|
|
{
|
|
struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev);
|
|
- struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
|
|
int irq_flags = irq_get_trigger_type(pdata->irq);
|
|
int ret;
|
|
|
|
@@ -235,8 +247,6 @@ static int rmi_irq_init(struct rmi_device *rmi_dev)
|
|
return ret;
|
|
}
|
|
|
|
- data->enabled = true;
|
|
-
|
|
return 0;
|
|
}
|
|
|
|
@@ -886,23 +896,27 @@ void rmi_enable_irq(struct rmi_device *rmi_dev, bool clear_wake)
|
|
if (data->enabled)
|
|
goto out;
|
|
|
|
- enable_irq(irq);
|
|
- data->enabled = true;
|
|
- if (clear_wake && device_may_wakeup(rmi_dev->xport->dev)) {
|
|
- retval = disable_irq_wake(irq);
|
|
- if (retval)
|
|
- dev_warn(&rmi_dev->dev,
|
|
- "Failed to disable irq for wake: %d\n",
|
|
- retval);
|
|
- }
|
|
+ if (irq) {
|
|
+ enable_irq(irq);
|
|
+ data->enabled = true;
|
|
+ if (clear_wake && device_may_wakeup(rmi_dev->xport->dev)) {
|
|
+ retval = disable_irq_wake(irq);
|
|
+ if (retval)
|
|
+ dev_warn(&rmi_dev->dev,
|
|
+ "Failed to disable irq for wake: %d\n",
|
|
+ retval);
|
|
+ }
|
|
|
|
- /*
|
|
- * Call rmi_process_interrupt_requests() after enabling irq,
|
|
- * otherwise we may lose interrupt on edge-triggered systems.
|
|
- */
|
|
- irq_flags = irq_get_trigger_type(pdata->irq);
|
|
- if (irq_flags & IRQ_TYPE_EDGE_BOTH)
|
|
- rmi_process_interrupt_requests(rmi_dev);
|
|
+ /*
|
|
+ * Call rmi_process_interrupt_requests() after enabling irq,
|
|
+ * otherwise we may lose interrupt on edge-triggered systems.
|
|
+ */
|
|
+ irq_flags = irq_get_trigger_type(pdata->irq);
|
|
+ if (irq_flags & IRQ_TYPE_EDGE_BOTH)
|
|
+ rmi_process_interrupt_requests(rmi_dev);
|
|
+ } else {
|
|
+ data->enabled = true;
|
|
+ }
|
|
|
|
out:
|
|
mutex_unlock(&data->enabled_mutex);
|
|
@@ -922,20 +936,22 @@ void rmi_disable_irq(struct rmi_device *rmi_dev, bool enable_wake)
|
|
goto out;
|
|
|
|
data->enabled = false;
|
|
- disable_irq(irq);
|
|
- if (enable_wake && device_may_wakeup(rmi_dev->xport->dev)) {
|
|
- retval = enable_irq_wake(irq);
|
|
- if (retval)
|
|
- dev_warn(&rmi_dev->dev,
|
|
- "Failed to enable irq for wake: %d\n",
|
|
- retval);
|
|
- }
|
|
-
|
|
- /* make sure the fifo is clean */
|
|
- while (!kfifo_is_empty(&data->attn_fifo)) {
|
|
- count = kfifo_get(&data->attn_fifo, &attn_data);
|
|
- if (count)
|
|
- kfree(attn_data.data);
|
|
+ if (irq) {
|
|
+ disable_irq(irq);
|
|
+ if (enable_wake && device_may_wakeup(rmi_dev->xport->dev)) {
|
|
+ retval = enable_irq_wake(irq);
|
|
+ if (retval)
|
|
+ dev_warn(&rmi_dev->dev,
|
|
+ "Failed to enable irq for wake: %d\n",
|
|
+ retval);
|
|
+ }
|
|
+ } else {
|
|
+ /* make sure the fifo is clean */
|
|
+ while (!kfifo_is_empty(&data->attn_fifo)) {
|
|
+ count = kfifo_get(&data->attn_fifo, &attn_data);
|
|
+ if (count)
|
|
+ kfree(attn_data.data);
|
|
+ }
|
|
}
|
|
|
|
out:
|
|
@@ -978,6 +994,8 @@ static int rmi_driver_remove(struct device *dev)
|
|
|
|
rmi_disable_irq(rmi_dev, false);
|
|
|
|
+ cancel_work_sync(&data->attn_work);
|
|
+
|
|
rmi_f34_remove_sysfs(rmi_dev);
|
|
rmi_free_function_list(rmi_dev);
|
|
|
|
@@ -1223,9 +1241,15 @@ static int rmi_driver_probe(struct device *dev)
|
|
}
|
|
}
|
|
|
|
- retval = rmi_irq_init(rmi_dev);
|
|
- if (retval < 0)
|
|
- goto err_destroy_functions;
|
|
+ if (pdata->irq) {
|
|
+ retval = rmi_irq_init(rmi_dev);
|
|
+ if (retval < 0)
|
|
+ goto err_destroy_functions;
|
|
+ }
|
|
+
|
|
+ data->enabled = true;
|
|
+
|
|
+ INIT_WORK(&data->attn_work, attn_callback);
|
|
|
|
if (data->f01_container->dev.driver) {
|
|
/* Driver already bound, so enable ATTN now. */
|
|
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
|
|
index 59244c744eabd..183f4eaab6e20 100644
|
|
--- a/drivers/iommu/iommu.c
|
|
+++ b/drivers/iommu/iommu.c
|
|
@@ -8,6 +8,7 @@
|
|
|
|
#include <linux/amba/bus.h>
|
|
#include <linux/device.h>
|
|
+#include <linux/dmi.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/bits.h>
|
|
#include <linux/bug.h>
|
|
@@ -2967,6 +2968,27 @@ int iommu_fwspec_add_ids(struct device *dev, const u32 *ids, int num_ids)
|
|
}
|
|
EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids);
|
|
|
|
+#ifdef CONFIG_ARM64
|
|
+static int __init iommu_quirks(void)
|
|
+{
|
|
+ const char *vendor, *name;
|
|
+
|
|
+ vendor = dmi_get_system_info(DMI_SYS_VENDOR);
|
|
+ name = dmi_get_system_info(DMI_PRODUCT_NAME);
|
|
+
|
|
+ if (vendor &&
|
|
+ (strncmp(vendor, "GIGABYTE", 8) == 0 && name &&
|
|
+ (strncmp(name, "R120", 4) == 0 ||
|
|
+ strncmp(name, "R270", 4) == 0))) {
|
|
+ pr_warn("Gigabyte %s detected, force iommu passthrough mode", name);
|
|
+ iommu_def_domain_type = IOMMU_DOMAIN_IDENTITY;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+arch_initcall(iommu_quirks);
|
|
+#endif
|
|
+
|
|
/**
|
|
* iommu_setup_default_domain - Set the default_domain for the group
|
|
* @group: Group to change
|
|
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
|
|
index 214ed060ca1b3..90a0bd985ad53 100644
|
|
--- a/drivers/pci/quirks.c
|
|
+++ b/drivers/pci/quirks.c
|
|
@@ -4451,6 +4451,30 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, 0x9000,
|
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, 0x9084,
|
|
quirk_bridge_cavm_thrx2_pcie_root);
|
|
|
|
+/*
|
|
+ * PCI BAR 5 is not setup correctly for the on-board AHCI controller
|
|
+ * on Broadcom's Vulcan processor. Added a quirk to fix BAR 5 by
|
|
+ * using BAR 4's resources which are populated correctly and NOT
|
|
+ * actually used by the AHCI controller.
|
|
+ */
|
|
+static void quirk_fix_vulcan_ahci_bars(struct pci_dev *dev)
|
|
+{
|
|
+ struct resource *r = &dev->resource[4];
|
|
+
|
|
+ if (!(r->flags & IORESOURCE_MEM) || (r->start == 0))
|
|
+ return;
|
|
+
|
|
+ /* Set BAR5 resource to BAR4 */
|
|
+ dev->resource[5] = *r;
|
|
+
|
|
+ /* Update BAR5 in pci config space */
|
|
+ pci_write_config_dword(dev, PCI_BASE_ADDRESS_5, r->start);
|
|
+
|
|
+ /* Clear BAR4's resource */
|
|
+ memset(r, 0, sizeof(*r));
|
|
+}
|
|
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, 0x9027, quirk_fix_vulcan_ahci_bars);
|
|
+
|
|
/*
|
|
* Intersil/Techwell TW686[4589]-based video capture cards have an empty (zero)
|
|
* class code. Fix it.
|
|
diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
|
|
index bdfb8a800c548..1505fc3ef7a85 100644
|
|
--- a/drivers/platform/x86/intel/int3472/discrete.c
|
|
+++ b/drivers/platform/x86/intel/int3472/discrete.c
|
|
@@ -129,6 +129,7 @@ skl_int3472_gpiod_get_from_temp_lookup(struct int3472_discrete_device *int3472,
|
|
* @hid: The ACPI HID of the device without the instance number e.g. INT347E
|
|
* @type_from: The GPIO type from ACPI ?SDT
|
|
* @type_to: The assigned GPIO type, typically same as @type_from
|
|
+ * @enable_time_us: Enable time in usec for GPIOs mapped to regulators
|
|
* @con_id: The name of the GPIO for the device
|
|
* @polarity_low: GPIO_ACTIVE_LOW true if the @polarity_low is true,
|
|
* GPIO_ACTIVE_HIGH otherwise
|
|
@@ -138,18 +139,36 @@ struct int3472_gpio_map {
|
|
u8 type_from;
|
|
u8 type_to;
|
|
bool polarity_low;
|
|
+ unsigned int enable_time_us;
|
|
const char *con_id;
|
|
};
|
|
|
|
static const struct int3472_gpio_map int3472_gpio_map[] = {
|
|
- /* mt9m114 designs declare a powerdown pin which controls the regulators */
|
|
- { "INT33F0", INT3472_GPIO_TYPE_POWERDOWN, INT3472_GPIO_TYPE_POWER_ENABLE, false, "vdd" },
|
|
- /* ov7251 driver / DT-bindings expect "enable" as con_id for reset */
|
|
- { "INT347E", INT3472_GPIO_TYPE_RESET, INT3472_GPIO_TYPE_RESET, false, "enable" },
|
|
+ { /* mt9m114 designs declare a powerdown pin which controls the regulators */
|
|
+ .hid = "INT33F0",
|
|
+ .type_from = INT3472_GPIO_TYPE_POWERDOWN,
|
|
+ .type_to = INT3472_GPIO_TYPE_POWER_ENABLE,
|
|
+ .con_id = "vdd",
|
|
+ .enable_time_us = GPIO_REGULATOR_ENABLE_TIME,
|
|
+ },
|
|
+ { /* ov7251 driver / DT-bindings expect "enable" as con_id for reset */
|
|
+ .hid = "INT347E",
|
|
+ .type_from = INT3472_GPIO_TYPE_RESET,
|
|
+ .type_to = INT3472_GPIO_TYPE_RESET,
|
|
+ .con_id = "enable",
|
|
+ },
|
|
+ { /* ov08x40's handshake pin needs a 45 ms delay on some HP laptops */
|
|
+ .hid = "OVTI08F4",
|
|
+ .type_from = INT3472_GPIO_TYPE_HANDSHAKE,
|
|
+ .type_to = INT3472_GPIO_TYPE_HANDSHAKE,
|
|
+ .con_id = "dvdd",
|
|
+ .enable_time_us = 45 * USEC_PER_MSEC,
|
|
+ },
|
|
};
|
|
|
|
static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3472, u8 *type,
|
|
- const char **con_id, unsigned long *gpio_flags)
|
|
+ const char **con_id, unsigned long *gpio_flags,
|
|
+ unsigned int *enable_time_us)
|
|
{
|
|
struct acpi_device *adev = int3472->sensor;
|
|
unsigned int i;
|
|
@@ -173,9 +192,12 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3
|
|
*gpio_flags = int3472_gpio_map[i].polarity_low ?
|
|
GPIO_ACTIVE_LOW : GPIO_ACTIVE_HIGH;
|
|
*con_id = int3472_gpio_map[i].con_id;
|
|
+ *enable_time_us = int3472_gpio_map[i].enable_time_us;
|
|
return;
|
|
}
|
|
|
|
+ *enable_time_us = GPIO_REGULATOR_ENABLE_TIME;
|
|
+
|
|
switch (*type) {
|
|
case INT3472_GPIO_TYPE_RESET:
|
|
*con_id = "reset";
|
|
@@ -204,6 +226,8 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3
|
|
case INT3472_GPIO_TYPE_HANDSHAKE:
|
|
*con_id = "dvdd";
|
|
*gpio_flags = GPIO_ACTIVE_HIGH;
|
|
+ /* Setups using a handshake pin need 25 ms enable delay */
|
|
+ *enable_time_us = 25 * USEC_PER_MSEC;
|
|
break;
|
|
default:
|
|
*con_id = "unknown";
|
|
@@ -249,13 +273,15 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
|
|
void *data)
|
|
{
|
|
struct int3472_discrete_device *int3472 = data;
|
|
+ const char *second_sensor = NULL;
|
|
struct acpi_resource_gpio *agpio;
|
|
+ unsigned int enable_time_us;
|
|
u8 active_value, pin, type;
|
|
+ unsigned long gpio_flags;
|
|
union acpi_object *obj;
|
|
struct gpio_desc *gpio;
|
|
const char *err_msg;
|
|
const char *con_id;
|
|
- unsigned long gpio_flags;
|
|
int ret;
|
|
|
|
if (!acpi_gpio_get_io_resource(ares, &agpio))
|
|
@@ -278,7 +304,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
|
|
|
|
type = FIELD_GET(INT3472_GPIO_DSM_TYPE, obj->integer.value);
|
|
|
|
- int3472_get_con_id_and_polarity(int3472, &type, &con_id, &gpio_flags);
|
|
+ int3472_get_con_id_and_polarity(int3472, &type, &con_id, &gpio_flags, &enable_time_us);
|
|
|
|
pin = FIELD_GET(INT3472_GPIO_DSM_PIN, obj->integer.value);
|
|
/* Pin field is not really used under Windows and wraps around at 8 bits */
|
|
@@ -328,21 +354,13 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
|
|
|
|
break;
|
|
case INT3472_GPIO_TYPE_POWER_ENABLE:
|
|
- ret = skl_int3472_register_regulator(int3472, gpio,
|
|
- GPIO_REGULATOR_ENABLE_TIME,
|
|
- con_id,
|
|
- int3472->quirks.avdd_second_sensor);
|
|
- if (ret)
|
|
- err_msg = "Failed to map power-enable to sensor\n";
|
|
-
|
|
- break;
|
|
+ second_sensor = int3472->quirks.avdd_second_sensor;
|
|
+ fallthrough;
|
|
case INT3472_GPIO_TYPE_HANDSHAKE:
|
|
- /* Setups using a handshake pin need 25 ms enable delay */
|
|
- ret = skl_int3472_register_regulator(int3472, gpio,
|
|
- 25 * USEC_PER_MSEC,
|
|
- con_id, NULL);
|
|
+ ret = skl_int3472_register_regulator(int3472, gpio, enable_time_us,
|
|
+ con_id, second_sensor);
|
|
if (ret)
|
|
- err_msg = "Failed to map handshake to sensor\n";
|
|
+ err_msg = "Failed to register regulator\n";
|
|
|
|
break;
|
|
default: /* Never reached */
|
|
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
|
|
index bf12e23f12121..009da46e7027b 100644
|
|
--- a/drivers/scsi/sd.c
|
|
+++ b/drivers/scsi/sd.c
|
|
@@ -121,6 +121,14 @@ static const char *sd_cache_types[] = {
|
|
"write back, no read (daft)"
|
|
};
|
|
|
|
+static const char *sd_probe_types[] = { "async", "sync" };
|
|
+
|
|
+static char sd_probe_type[6] = "async";
|
|
+module_param_string(probe, sd_probe_type, sizeof(sd_probe_type),
|
|
+ S_IRUGO|S_IWUSR);
|
|
+MODULE_PARM_DESC(probe, "async or sync. Setting to 'sync' disables asynchronous "
|
|
+ "device number assignments (sda, sdb, ...).");
|
|
+
|
|
static void sd_set_flush_flag(struct scsi_disk *sdkp,
|
|
struct queue_limits *lim)
|
|
{
|
|
@@ -4379,6 +4387,8 @@ static int __init init_sd(void)
|
|
goto err_out_class;
|
|
}
|
|
|
|
+ if (!strcmp(sd_probe_type, "sync"))
|
|
+ sd_template.gendrv.probe_type = PROBE_FORCE_SYNCHRONOUS;
|
|
err = scsi_register_driver(&sd_template.gendrv);
|
|
if (err)
|
|
goto err_out_driver;
|
|
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
|
|
index 256fe8c86828d..2e4a09948df8f 100644
|
|
--- a/drivers/usb/core/hub.c
|
|
+++ b/drivers/usb/core/hub.c
|
|
@@ -5893,6 +5893,13 @@ static void hub_event(struct work_struct *work)
|
|
(u16) hub->change_bits[0],
|
|
(u16) hub->event_bits[0]);
|
|
|
|
+ /* Don't disconnect USB-SATA on TrimSlice */
|
|
+ if (strcmp(dev_name(hdev->bus->controller), "tegra-ehci.0") == 0) {
|
|
+ if ((hdev->state == 7) && (hub->change_bits[0] == 0) &&
|
|
+ (hub->event_bits[0] == 0x2))
|
|
+ hub->event_bits[0] = 0;
|
|
+ }
|
|
+
|
|
/* Lock the device, then check to see if we were
|
|
* disconnected while waiting for the lock to succeed. */
|
|
usb_lock_device(hdev);
|
|
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
|
|
index 9bf8fc6247bac..7cfcdb6284180 100644
|
|
--- a/drivers/usb/misc/Kconfig
|
|
+++ b/drivers/usb/misc/Kconfig
|
|
@@ -179,6 +179,20 @@ config USB_LJCA
|
|
This driver can also be built as a module. If so, the module
|
|
will be called usb-ljca.
|
|
|
|
+config USB_USBIO
|
|
+ tristate "Intel USBIO Bridge support"
|
|
+ depends on USB && ACPI
|
|
+ select AUXILIARY_BUS
|
|
+ help
|
|
+ This adds support for Intel USBIO drivers.
|
|
+ This enables the USBIO bridge driver module in charge to talk
|
|
+ to the USB device. Additional drivers such as GPIO_USBIO and
|
|
+ I2C_USBIO must be enabled in order to use the device's full
|
|
+ functionality.
|
|
+
|
|
+ This driver can also be built as a module. If so, the module
|
|
+ will be called usbio.
|
|
+
|
|
source "drivers/usb/misc/sisusbvga/Kconfig"
|
|
|
|
config USB_LD
|
|
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
|
|
index 0cd5bc8f52fe0..494ab0377f356 100644
|
|
--- a/drivers/usb/misc/Makefile
|
|
+++ b/drivers/usb/misc/Makefile
|
|
@@ -12,6 +12,7 @@ obj-$(CONFIG_USB_EMI62) += emi62.o
|
|
obj-$(CONFIG_USB_EZUSB_FX2) += ezusb.o
|
|
obj-$(CONFIG_APPLE_MFI_FASTCHARGE) += apple-mfi-fastcharge.o
|
|
obj-$(CONFIG_USB_LJCA) += usb-ljca.o
|
|
+obj-$(CONFIG_USB_USBIO) += usbio.o
|
|
obj-$(CONFIG_USB_IDMOUSE) += idmouse.o
|
|
obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o
|
|
obj-$(CONFIG_USB_ISIGHTFW) += isight_firmware.o
|
|
diff --git a/drivers/usb/misc/usbio.c b/drivers/usb/misc/usbio.c
|
|
new file mode 100644
|
|
index 0000000000000..37644dddf157e
|
|
--- /dev/null
|
|
+++ b/drivers/usb/misc/usbio.c
|
|
@@ -0,0 +1,749 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * Intel USBIO Bridge driver
|
|
+ *
|
|
+ * Copyright (c) 2025 Intel Corporation.
|
|
+ * Copyright (c) 2025 Red Hat, Inc.
|
|
+ */
|
|
+
|
|
+#include <linux/acpi.h>
|
|
+#include <linux/auxiliary_bus.h>
|
|
+#include <linux/byteorder/generic.h>
|
|
+#include <linux/cleanup.h>
|
|
+#include <linux/completion.h>
|
|
+#include <linux/dev_printk.h>
|
|
+#include <linux/device.h>
|
|
+#include <linux/lockdep.h>
|
|
+#include <linux/mutex.h>
|
|
+#include <linux/string.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/usb.h>
|
|
+#include <linux/usb/usbio.h>
|
|
+
|
|
+/*************************************
|
|
+ * USBIO Bridge Protocol Definitions *
|
|
+ *************************************/
|
|
+
|
|
+/* USBIO Control Commands */
|
|
+#define USBIO_CTRLCMD_PROTVER 0
|
|
+#define USBIO_CTRLCMD_FWVER 1
|
|
+#define USBIO_CTRLCMD_HS 2
|
|
+#define USBIO_CTRLCMD_ENUMGPIO 16
|
|
+#define USBIO_CTRLCMD_ENUMI2C 17
|
|
+
|
|
+/* USBIO Packet Flags */
|
|
+#define USBIO_PKTFLAG_ACK BIT(0)
|
|
+#define USBIO_PKTFLAG_RSP BIT(1)
|
|
+#define USBIO_PKTFLAG_CMP BIT(2)
|
|
+#define USBIO_PKTFLAG_ERR BIT(3)
|
|
+
|
|
+#define USBIO_PKTFLAGS_REQRESP (USBIO_PKTFLAG_CMP | USBIO_PKTFLAG_ACK)
|
|
+
|
|
+#define USBIO_CTRLXFER_TIMEOUT 0
|
|
+#define USBIO_BULKXFER_TIMEOUT 100
|
|
+
|
|
+struct usbio_protver {
|
|
+ u8 ver;
|
|
+} __packed;
|
|
+
|
|
+struct usbio_fwver {
|
|
+ u8 major;
|
|
+ u8 minor;
|
|
+ __le16 patch;
|
|
+ __le16 build;
|
|
+} __packed;
|
|
+
|
|
+/***********************************
|
|
+ * USBIO Bridge Device Definitions *
|
|
+ ***********************************/
|
|
+
|
|
+/**
|
|
+ * struct usbio_device - the usb device exposing IOs
|
|
+ *
|
|
+ * @dev: the device in the usb interface
|
|
+ * @udev: the detected usb device
|
|
+ * @intf: the usb interface
|
|
+ * @quirks: quirks
|
|
+ * @ctrl_mutex: protects ctrl_buf
|
|
+ * @ctrl_pipe: the control transfer pipe
|
|
+ * @ctrlbuf_len: the size of the control transfer pipe
|
|
+ * @ctrlbuf: the buffer used for control transfers
|
|
+ * @bulk_mutex: protects tx_buf, rx_buf and split bulk-transfers getting interrupted
|
|
+ * @tx_pipe: the bulk out pipe
|
|
+ * @txbuf_len: the size of the bulk out pipe
|
|
+ * @txbuf: the buffer used for bulk out transfers
|
|
+ * @rx_pipe: the bulk in pipe
|
|
+ * @rxbuf_len: the size of the bulk in pipe
|
|
+ * @rxdat_len: the data length at rx buffer
|
|
+ * @rxbuf: the buffer used for bulk in transfers
|
|
+ * @urb: the urb to read bulk pipe
|
|
+ * @done: completion object as request is done
|
|
+ * @cli_list: device's client list
|
|
+ * @nr_gpio_banks: Number of GPIO banks
|
|
+ * @gpios: GPIO bank descriptors
|
|
+ * @nr_gpio_banks: Number of I2C busses
|
|
+ * @gpios: I2C bank descriptors
|
|
+ */
|
|
+struct usbio_device {
|
|
+ struct device *dev;
|
|
+ struct usb_device *udev;
|
|
+ struct usb_interface *intf;
|
|
+ unsigned long quirks;
|
|
+
|
|
+ struct mutex ctrl_mutex;
|
|
+ unsigned int ctrl_pipe;
|
|
+ u16 ctrlbuf_len;
|
|
+ void *ctrlbuf;
|
|
+
|
|
+ struct mutex bulk_mutex;
|
|
+ unsigned int tx_pipe;
|
|
+ u16 txbuf_len;
|
|
+ void *txbuf;
|
|
+
|
|
+ unsigned int rx_pipe;
|
|
+ u16 rxbuf_len;
|
|
+ u16 rxdat_len;
|
|
+ void *rxbuf;
|
|
+ struct urb *urb;
|
|
+
|
|
+ struct completion done;
|
|
+
|
|
+ struct list_head cli_list;
|
|
+
|
|
+ unsigned int nr_gpio_banks;
|
|
+ struct usbio_gpio_bank_desc gpios[USBIO_MAX_GPIOBANKS];
|
|
+
|
|
+ unsigned int nr_i2c_buses;
|
|
+ struct usbio_i2c_bus_desc i2cs[USBIO_MAX_I2CBUSES];
|
|
+};
|
|
+
|
|
+/**
|
|
+ * struct usbio_client - represents a usbio client
|
|
+ *
|
|
+ * @auxdev: auxiliary device object
|
|
+ * @mutex: protects @bridge
|
|
+ * @bridge: usbio bridge who service the client
|
|
+ * @link: usbio bridge clients list member
|
|
+ */
|
|
+struct usbio_client {
|
|
+ struct auxiliary_device auxdev;
|
|
+ struct mutex mutex;
|
|
+ struct usbio_device *bridge;
|
|
+ struct list_head link;
|
|
+};
|
|
+
|
|
+#define adev_to_client(adev) container_of_const(adev, struct usbio_client, auxdev)
|
|
+
|
|
+static int usbio_ctrl_msg(struct usbio_device *usbio, u8 type, u8 cmd,
|
|
+ const void *obuf, u16 obuf_len, void *ibuf, u16 ibuf_len)
|
|
+{
|
|
+ u8 request = USB_TYPE_VENDOR | USB_RECIP_DEVICE;
|
|
+ struct usbio_ctrl_packet *cpkt;
|
|
+ unsigned int pipe;
|
|
+ u16 cpkt_len;
|
|
+ int ret;
|
|
+
|
|
+ lockdep_assert_held(&usbio->ctrl_mutex);
|
|
+
|
|
+ if ((obuf_len > (usbio->ctrlbuf_len - sizeof(*cpkt))) ||
|
|
+ (ibuf_len > (usbio->ctrlbuf_len - sizeof(*cpkt))))
|
|
+ return -EMSGSIZE;
|
|
+
|
|
+ /* Prepare Control Packet Header */
|
|
+ cpkt = usbio->ctrlbuf;
|
|
+ cpkt->header.type = type;
|
|
+ cpkt->header.cmd = cmd;
|
|
+ if (type == USBIO_PKTTYPE_CTRL || ibuf_len)
|
|
+ cpkt->header.flags = USBIO_PKTFLAGS_REQRESP;
|
|
+ else
|
|
+ cpkt->header.flags = USBIO_PKTFLAG_CMP;
|
|
+ cpkt->len = obuf_len;
|
|
+
|
|
+ /* Copy the data */
|
|
+ memcpy(cpkt->data, obuf, obuf_len);
|
|
+
|
|
+ pipe = usb_sndctrlpipe(usbio->udev, usbio->ctrl_pipe);
|
|
+ cpkt_len = sizeof(*cpkt) + obuf_len;
|
|
+ ret = usb_control_msg(usbio->udev, pipe, 0, request | USB_DIR_OUT, 0, 0,
|
|
+ cpkt, cpkt_len, USBIO_CTRLXFER_TIMEOUT);
|
|
+ dev_dbg(usbio->dev, "control out %d hdr %*phN data %*phN\n", ret,
|
|
+ (int)sizeof(*cpkt), cpkt, (int)cpkt->len, cpkt->data);
|
|
+
|
|
+ if (ret != cpkt_len) {
|
|
+ dev_err(usbio->dev, "USB control out failed: %d\n", ret);
|
|
+ return (ret < 0) ? ret : -EPROTO;
|
|
+ }
|
|
+
|
|
+ if (!(cpkt->header.flags & USBIO_PKTFLAG_ACK))
|
|
+ return 0;
|
|
+
|
|
+ pipe = usb_rcvctrlpipe(usbio->udev, usbio->ctrl_pipe);
|
|
+ cpkt_len = sizeof(*cpkt) + ibuf_len;
|
|
+ ret = usb_control_msg(usbio->udev, pipe, 0, request | USB_DIR_IN, 0, 0,
|
|
+ cpkt, cpkt_len, USBIO_CTRLXFER_TIMEOUT);
|
|
+ dev_dbg(usbio->dev, "control in %d hdr %*phN data %*phN\n", ret,
|
|
+ (int)sizeof(*cpkt), cpkt, (int)cpkt->len, cpkt->data);
|
|
+
|
|
+ if (ret < sizeof(*cpkt)) {
|
|
+ dev_err(usbio->dev, "USB control in failed: %d\n", ret);
|
|
+ return (ret < 0) ? ret : -EPROTO;
|
|
+ }
|
|
+
|
|
+ if (cpkt->header.type != type || cpkt->header.cmd != cmd ||
|
|
+ !(cpkt->header.flags & USBIO_PKTFLAG_RSP)) {
|
|
+ dev_err(usbio->dev, "Unexpected reply type: %u, cmd: %u, flags: %u\n",
|
|
+ cpkt->header.type, cpkt->header.cmd, cpkt->header.flags);
|
|
+ return -EPROTO;
|
|
+ }
|
|
+
|
|
+ if (cpkt->header.flags & USBIO_PKTFLAG_ERR)
|
|
+ return -EREMOTEIO;
|
|
+
|
|
+ if (ibuf_len < cpkt->len)
|
|
+ return -ENOSPC;
|
|
+
|
|
+ memcpy(ibuf, cpkt->data, cpkt->len);
|
|
+
|
|
+ return cpkt->len;
|
|
+}
|
|
+
|
|
+int usbio_control_msg(struct auxiliary_device *adev, u8 type, u8 cmd,
|
|
+ const void *obuf, u16 obuf_len, void *ibuf, u16 ibuf_len)
|
|
+{
|
|
+ struct usbio_client *client = adev_to_client(adev);
|
|
+ struct usbio_device *usbio;
|
|
+ int ret;
|
|
+
|
|
+ guard(mutex)(&client->mutex);
|
|
+
|
|
+ usbio = client->bridge;
|
|
+ if (!usbio)
|
|
+ return -ENODEV; /* Disconnected */
|
|
+
|
|
+ ret = usb_autopm_get_interface(usbio->intf);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ mutex_lock(&usbio->ctrl_mutex);
|
|
+
|
|
+ ret = usbio_ctrl_msg(client->bridge, type, cmd, obuf, obuf_len, ibuf, ibuf_len);
|
|
+
|
|
+ mutex_unlock(&usbio->ctrl_mutex);
|
|
+ usb_autopm_put_interface(usbio->intf);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL_NS_GPL(usbio_control_msg, "USBIO");
|
|
+
|
|
+static void usbio_bulk_recv(struct urb *urb)
|
|
+{
|
|
+ struct usbio_bulk_packet *bpkt = urb->transfer_buffer;
|
|
+ struct usbio_device *usbio = urb->context;
|
|
+
|
|
+ if (!urb->status) {
|
|
+ if (bpkt->header.flags & USBIO_PKTFLAG_RSP) {
|
|
+ usbio->rxdat_len = urb->actual_length;
|
|
+ complete(&usbio->done);
|
|
+ }
|
|
+ } else if (urb->status != -ENOENT) {
|
|
+ dev_err(usbio->dev, "Bulk in error %d\n", urb->status);
|
|
+ }
|
|
+
|
|
+ usb_submit_urb(usbio->urb, GFP_ATOMIC);
|
|
+}
|
|
+
|
|
+int usbio_bulk_msg(struct auxiliary_device *adev, u8 type, u8 cmd, bool last,
|
|
+ const void *obuf, u16 obuf_len, void *ibuf, u16 ibuf_len)
|
|
+{
|
|
+ struct usbio_client *client = adev_to_client(adev);
|
|
+ struct usbio_device *usbio = client->bridge;
|
|
+ struct usbio_bulk_packet *bpkt;
|
|
+ int ret, act = 0;
|
|
+ u16 bpkt_len;
|
|
+
|
|
+ lockdep_assert_held(&client->mutex);
|
|
+ lockdep_assert_held(&usbio->bulk_mutex);
|
|
+
|
|
+ if ((obuf_len > (usbio->txbuf_len - sizeof(*bpkt))) ||
|
|
+ (ibuf_len > (usbio->txbuf_len - sizeof(*bpkt))))
|
|
+ return -EMSGSIZE;
|
|
+
|
|
+ if (ibuf_len)
|
|
+ reinit_completion(&usbio->done);
|
|
+
|
|
+ /* If no data to send, skip to read */
|
|
+ if (!obuf_len)
|
|
+ goto read;
|
|
+
|
|
+ /* Prepare Bulk Packet Header */
|
|
+ bpkt = usbio->txbuf;
|
|
+ bpkt->header.type = type;
|
|
+ bpkt->header.cmd = cmd;
|
|
+ if (!last)
|
|
+ bpkt->header.flags = 0;
|
|
+ else if (ibuf_len)
|
|
+ bpkt->header.flags = USBIO_PKTFLAGS_REQRESP;
|
|
+ else
|
|
+ bpkt->header.flags = USBIO_PKTFLAG_CMP;
|
|
+ bpkt->len = cpu_to_le16(obuf_len);
|
|
+
|
|
+ /* Copy the data */
|
|
+ memcpy(bpkt->data, obuf, obuf_len);
|
|
+
|
|
+ bpkt_len = sizeof(*bpkt) + obuf_len;
|
|
+ ret = usb_bulk_msg(usbio->udev, usbio->tx_pipe, bpkt, bpkt_len, &act,
|
|
+ USBIO_BULKXFER_TIMEOUT);
|
|
+ dev_dbg(usbio->dev, "bulk out %d hdr %*phN data %*phN\n", act,
|
|
+ (int)sizeof(*bpkt), bpkt, obuf_len, bpkt->data);
|
|
+
|
|
+ if (ret || act != bpkt_len) {
|
|
+ dev_err(usbio->dev, "Bulk out failed: %d\n", ret);
|
|
+ return ret ?: -EPROTO;
|
|
+ }
|
|
+
|
|
+ if (!(bpkt->header.flags & USBIO_PKTFLAG_ACK))
|
|
+ return obuf_len;
|
|
+
|
|
+read:
|
|
+ ret = wait_for_completion_timeout(&usbio->done, USBIO_BULKXFER_TIMEOUT);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(usbio->dev, "Bulk in wait failed: %d\n", ret);
|
|
+ return ret ?: -ETIMEDOUT;
|
|
+ }
|
|
+
|
|
+ act = usbio->rxdat_len;
|
|
+ bpkt = usbio->rxbuf;
|
|
+ bpkt_len = le16_to_cpu(bpkt->len);
|
|
+ dev_dbg(usbio->dev, "bulk in %d hdr %*phN data %*phN\n", act,
|
|
+ (int)sizeof(*bpkt), bpkt, bpkt_len, bpkt->data);
|
|
+
|
|
+ /*
|
|
+ * Unsupported bulk commands get only an usbio_packet_header with
|
|
+ * the error flag set as reply. Return -EPIPE for this case.
|
|
+ */
|
|
+ if (act == sizeof(struct usbio_packet_header) &&
|
|
+ (bpkt->header.flags & USBIO_PKTFLAG_ERR))
|
|
+ return -EPIPE;
|
|
+
|
|
+ if (act < sizeof(*bpkt)) {
|
|
+ dev_err(usbio->dev, "Bulk in short read: %d\n", act);
|
|
+ return -EPROTO;
|
|
+ }
|
|
+
|
|
+ if (bpkt->header.type != type || bpkt->header.cmd != cmd ||
|
|
+ !(bpkt->header.flags & USBIO_PKTFLAG_RSP)) {
|
|
+ dev_err(usbio->dev,
|
|
+ "Unexpected bulk in type 0x%02x cmd 0x%02x flags 0x%02x\n",
|
|
+ bpkt->header.type, bpkt->header.cmd, bpkt->header.flags);
|
|
+ return -EPROTO;
|
|
+ }
|
|
+
|
|
+ if (bpkt->header.flags & USBIO_PKTFLAG_ERR)
|
|
+ return -EREMOTEIO;
|
|
+
|
|
+ if (ibuf_len < bpkt_len)
|
|
+ return -ENOSPC;
|
|
+
|
|
+ memcpy(ibuf, bpkt->data, bpkt_len);
|
|
+
|
|
+ return bpkt_len;
|
|
+}
|
|
+EXPORT_SYMBOL_NS_GPL(usbio_bulk_msg, "USBIO");
|
|
+
|
|
+int usbio_acquire(struct auxiliary_device *adev)
|
|
+{
|
|
+ struct usbio_client *client = adev_to_client(adev);
|
|
+ struct usbio_device *usbio;
|
|
+ int ret;
|
|
+
|
|
+ mutex_lock(&client->mutex);
|
|
+
|
|
+ usbio = client->bridge;
|
|
+ if (!usbio) {
|
|
+ ret = -ENODEV; /* Disconnected */
|
|
+ goto err_unlock;
|
|
+ }
|
|
+
|
|
+ ret = usb_autopm_get_interface(usbio->intf);
|
|
+ if (ret)
|
|
+ goto err_unlock;
|
|
+
|
|
+ mutex_lock(&usbio->bulk_mutex);
|
|
+
|
|
+ /* Leave client locked until release to avoid abba deadlock issues */
|
|
+ return 0;
|
|
+
|
|
+err_unlock:
|
|
+ mutex_unlock(&client->mutex);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL_NS_GPL(usbio_acquire, "USBIO");
|
|
+
|
|
+void usbio_release(struct auxiliary_device *adev)
|
|
+{
|
|
+ struct usbio_client *client = adev_to_client(adev);
|
|
+ struct usbio_device *usbio = client->bridge;
|
|
+
|
|
+ lockdep_assert_held(&client->mutex);
|
|
+
|
|
+ mutex_unlock(&usbio->bulk_mutex);
|
|
+ usb_autopm_put_interface(usbio->intf);
|
|
+ mutex_unlock(&client->mutex);
|
|
+}
|
|
+EXPORT_SYMBOL_NS_GPL(usbio_release, "USBIO");
|
|
+
|
|
+void usbio_get_txrxbuf_len(struct auxiliary_device *adev, u16 *txbuf_len, u16 *rxbuf_len)
|
|
+{
|
|
+ struct usbio_client *client = adev_to_client(adev);
|
|
+ struct usbio_device *usbio;
|
|
+
|
|
+ guard(mutex)(&client->mutex);
|
|
+
|
|
+ usbio = client->bridge;
|
|
+ if (!usbio)
|
|
+ return; /* Disconnected */
|
|
+
|
|
+ *txbuf_len = usbio->txbuf_len;
|
|
+ *rxbuf_len = usbio->rxbuf_len;
|
|
+}
|
|
+EXPORT_SYMBOL_NS_GPL(usbio_get_txrxbuf_len, "USBIO");
|
|
+
|
|
+unsigned long usbio_get_quirks(struct auxiliary_device *adev)
|
|
+{
|
|
+ struct usbio_client *client = adev_to_client(adev);
|
|
+ struct usbio_device *usbio;
|
|
+
|
|
+ guard(mutex)(&client->mutex);
|
|
+
|
|
+ usbio = client->bridge;
|
|
+ if (!usbio)
|
|
+ return 0; /* Disconnected */
|
|
+
|
|
+ return usbio->quirks;
|
|
+}
|
|
+EXPORT_SYMBOL_NS_GPL(usbio_get_quirks, "USBIO");
|
|
+
|
|
+static void usbio_auxdev_release(struct device *dev)
|
|
+{
|
|
+ struct auxiliary_device *adev = to_auxiliary_dev(dev);
|
|
+ struct usbio_client *client = adev_to_client(adev);
|
|
+
|
|
+ mutex_destroy(&client->mutex);
|
|
+ kfree(client);
|
|
+}
|
|
+
|
|
+static int usbio_add_client(struct usbio_device *usbio, char *name, u8 id, void *data)
|
|
+{
|
|
+ struct usbio_client *client;
|
|
+ struct auxiliary_device *adev;
|
|
+ int ret;
|
|
+
|
|
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
|
|
+ if (!client)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ mutex_init(&client->mutex);
|
|
+ client->bridge = usbio;
|
|
+ adev = &client->auxdev;
|
|
+ adev->name = name;
|
|
+ adev->id = id;
|
|
+
|
|
+ adev->dev.parent = usbio->dev;
|
|
+ adev->dev.platform_data = data;
|
|
+ adev->dev.release = usbio_auxdev_release;
|
|
+
|
|
+ ret = auxiliary_device_init(adev);
|
|
+ if (ret) {
|
|
+ usbio_auxdev_release(&adev->dev);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = auxiliary_device_add(adev);
|
|
+ if (ret) {
|
|
+ auxiliary_device_uninit(adev);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ list_add_tail(&client->link, &usbio->cli_list);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int usbio_enum_gpios(struct usbio_device *usbio)
|
|
+{
|
|
+ struct usbio_gpio_bank_desc *gpio = usbio->gpios;
|
|
+
|
|
+ dev_dbg(usbio->dev, "GPIO Banks: %d\n", usbio->nr_gpio_banks);
|
|
+
|
|
+ for (unsigned int i = 0; i < usbio->nr_gpio_banks; i++)
|
|
+ dev_dbg(usbio->dev, "\tBank%d[%d] map: %#08x\n",
|
|
+ gpio[i].id, gpio[i].pins, gpio[i].bmap);
|
|
+
|
|
+ usbio_add_client(usbio, USBIO_GPIO_CLIENT, 0, gpio);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int usbio_enum_i2cs(struct usbio_device *usbio)
|
|
+{
|
|
+ struct usbio_i2c_bus_desc *i2c = usbio->i2cs;
|
|
+
|
|
+ dev_dbg(usbio->dev, "I2C Busses: %d\n", usbio->nr_i2c_buses);
|
|
+
|
|
+ for (unsigned int i = 0; i < usbio->nr_i2c_buses; i++) {
|
|
+ dev_dbg(usbio->dev, "\tBus%d caps: %#02x\n", i2c[i].id, i2c[i].caps);
|
|
+ usbio_add_client(usbio, USBIO_I2C_CLIENT, i, &i2c[i]);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int usbio_suspend(struct usb_interface *intf, pm_message_t msg)
|
|
+{
|
|
+ struct usbio_device *usbio = usb_get_intfdata(intf);
|
|
+
|
|
+ usb_kill_urb(usbio->urb);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int usbio_resume(struct usb_interface *intf)
|
|
+{
|
|
+ struct usbio_device *usbio = usb_get_intfdata(intf);
|
|
+
|
|
+ return usb_submit_urb(usbio->urb, GFP_KERNEL);
|
|
+}
|
|
+
|
|
+static void usbio_disconnect(struct usb_interface *intf)
|
|
+{
|
|
+ struct usbio_device *usbio = usb_get_intfdata(intf);
|
|
+ struct usbio_client *client;
|
|
+
|
|
+ /* Wakeup any clients waiting for a reply */
|
|
+ usbio->rxdat_len = 0;
|
|
+ complete(&usbio->done);
|
|
+
|
|
+ /* Let clients know the bridge is gone */
|
|
+ list_for_each_entry(client, &usbio->cli_list, link) {
|
|
+ mutex_lock(&client->mutex);
|
|
+ client->bridge = NULL;
|
|
+ mutex_unlock(&client->mutex);
|
|
+ }
|
|
+
|
|
+ /* From here on clients will no longer touch struct usbio_device */
|
|
+ usb_kill_urb(usbio->urb);
|
|
+ usb_free_urb(usbio->urb);
|
|
+
|
|
+ list_for_each_entry_reverse(client, &usbio->cli_list, link) {
|
|
+ auxiliary_device_delete(&client->auxdev);
|
|
+ auxiliary_device_uninit(&client->auxdev);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int usbio_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
|
+{
|
|
+ struct usb_device *udev = interface_to_usbdev(intf);
|
|
+ struct usb_endpoint_descriptor *ep_in, *ep_out;
|
|
+ struct device *dev = &intf->dev;
|
|
+ struct usbio_protver protver;
|
|
+ struct usbio_device *usbio;
|
|
+ struct usbio_fwver fwver;
|
|
+ int ret;
|
|
+
|
|
+ usbio = devm_kzalloc(dev, sizeof(*usbio), GFP_KERNEL);
|
|
+ if (!usbio)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ret = devm_mutex_init(dev, &usbio->ctrl_mutex);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = devm_mutex_init(dev, &usbio->bulk_mutex);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ usbio->dev = dev;
|
|
+ usbio->udev = udev;
|
|
+ usbio->intf = intf;
|
|
+ usbio->quirks = id ? id->driver_info : 0;
|
|
+ init_completion(&usbio->done);
|
|
+ INIT_LIST_HEAD(&usbio->cli_list);
|
|
+ usb_set_intfdata(intf, usbio);
|
|
+
|
|
+ usbio->ctrl_pipe = usb_endpoint_num(&udev->ep0.desc);
|
|
+ usbio->ctrlbuf_len = usb_maxpacket(udev, usbio->ctrl_pipe);
|
|
+ usbio->ctrlbuf = devm_kzalloc(dev, usbio->ctrlbuf_len, GFP_KERNEL);
|
|
+ if (!usbio->ctrlbuf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* Find the first bulk-in and bulk-out endpoints */
|
|
+ ret = usb_find_common_endpoints(intf->cur_altsetting, &ep_in, &ep_out,
|
|
+ NULL, NULL);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "Cannot find bulk endpoints: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ usbio->tx_pipe = usb_sndbulkpipe(udev, usb_endpoint_num(ep_out));
|
|
+
|
|
+ if (usbio->quirks & USBIO_QUIRK_BULK_MAXP_63)
|
|
+ usbio->txbuf_len = 63;
|
|
+ else
|
|
+ usbio->txbuf_len = usb_endpoint_maxp(ep_out);
|
|
+
|
|
+ usbio->txbuf = devm_kzalloc(dev, usbio->txbuf_len, GFP_KERNEL);
|
|
+ if (!usbio->txbuf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ usbio->rx_pipe = usb_rcvbulkpipe(udev, usb_endpoint_num(ep_in));
|
|
+
|
|
+ if (usbio->quirks & USBIO_QUIRK_BULK_MAXP_63)
|
|
+ usbio->rxbuf_len = 63;
|
|
+ else
|
|
+ usbio->rxbuf_len = usb_endpoint_maxp(ep_in);
|
|
+
|
|
+ usbio->rxbuf = devm_kzalloc(dev, usbio->rxbuf_len, GFP_KERNEL);
|
|
+ if (!usbio->rxbuf)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ usbio->urb = usb_alloc_urb(0, GFP_KERNEL);
|
|
+ if (!usbio->urb)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ usb_fill_bulk_urb(usbio->urb, udev, usbio->rx_pipe, usbio->rxbuf,
|
|
+ usbio->rxbuf_len, usbio_bulk_recv, usbio);
|
|
+ ret = usb_submit_urb(usbio->urb, GFP_KERNEL);
|
|
+ if (ret)
|
|
+ return dev_err_probe(dev, ret, "Submitting usb urb\n");
|
|
+
|
|
+ mutex_lock(&usbio->ctrl_mutex);
|
|
+
|
|
+ ret = usbio_ctrl_msg(usbio, USBIO_PKTTYPE_CTRL, USBIO_CTRLCMD_HS, NULL, 0, NULL, 0);
|
|
+ if (ret < 0)
|
|
+ goto err_unlock;
|
|
+
|
|
+ ret = usbio_ctrl_msg(usbio, USBIO_PKTTYPE_CTRL, USBIO_CTRLCMD_PROTVER, NULL, 0,
|
|
+ &protver, sizeof(protver));
|
|
+ if (ret < 0)
|
|
+ goto err_unlock;
|
|
+
|
|
+ ret = usbio_ctrl_msg(usbio, USBIO_PKTTYPE_CTRL, USBIO_CTRLCMD_FWVER, NULL, 0,
|
|
+ &fwver, sizeof(fwver));
|
|
+ if (ret < 0)
|
|
+ goto err_unlock;
|
|
+
|
|
+ ret = usbio_ctrl_msg(usbio, USBIO_PKTTYPE_CTRL, USBIO_CTRLCMD_ENUMGPIO, NULL, 0,
|
|
+ usbio->gpios, sizeof(usbio->gpios));
|
|
+ if (ret < 0 || ret % sizeof(struct usbio_gpio_bank_desc)) {
|
|
+ ret = (ret < 0) ? ret : -EPROTO;
|
|
+ goto err_unlock;
|
|
+ }
|
|
+ usbio->nr_gpio_banks = ret / sizeof(struct usbio_gpio_bank_desc);
|
|
+
|
|
+ ret = usbio_ctrl_msg(usbio, USBIO_PKTTYPE_CTRL, USBIO_CTRLCMD_ENUMI2C, NULL, 0,
|
|
+ usbio->i2cs, sizeof(usbio->i2cs));
|
|
+ if (ret < 0 || ret % sizeof(struct usbio_i2c_bus_desc)) {
|
|
+ ret = (ret < 0) ? ret : -EPROTO;
|
|
+ goto err_unlock;
|
|
+ }
|
|
+ usbio->nr_i2c_buses = ret / sizeof(struct usbio_i2c_bus_desc);
|
|
+
|
|
+ mutex_unlock(&usbio->ctrl_mutex);
|
|
+
|
|
+ dev_dbg(dev, "ProtVer(BCD): %02x FwVer: %d.%d.%d.%d\n",
|
|
+ protver.ver, fwver.major, fwver.minor,
|
|
+ le16_to_cpu(fwver.patch), le16_to_cpu(fwver.build));
|
|
+
|
|
+ usbio_enum_gpios(usbio);
|
|
+ usbio_enum_i2cs(usbio);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_unlock:
|
|
+ mutex_unlock(&usbio->ctrl_mutex);
|
|
+ usb_kill_urb(usbio->urb);
|
|
+ usb_free_urb(usbio->urb);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static const struct usb_device_id usbio_table[] = {
|
|
+ { USB_DEVICE(0x2ac1, 0x20c1), /* Lattice NX40 */
|
|
+ .driver_info = USBIO_QUIRK_I2C_MAX_RW_LEN_52 },
|
|
+ { USB_DEVICE(0x2ac1, 0x20c9), /* Lattice NX33 */
|
|
+ .driver_info = USBIO_QUIRK_I2C_NO_INIT_ACK | USBIO_QUIRK_I2C_MAX_RW_LEN_52 |
|
|
+ USBIO_QUIRK_I2C_ALLOW_400KHZ },
|
|
+ { USB_DEVICE(0x2ac1, 0x20cb) }, /* Lattice NX33U */
|
|
+ { USB_DEVICE(0x06cb, 0x0701), /* Synaptics Sabre */
|
|
+ .driver_info = USBIO_QUIRK_BULK_MAXP_63 | USBIO_QUIRK_I2C_USE_CHUNK_LEN },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(usb, usbio_table);
|
|
+
|
|
+static struct usb_driver usbio_driver = {
|
|
+ .name = "usbio-bridge",
|
|
+ .probe = usbio_probe,
|
|
+ .disconnect = usbio_disconnect,
|
|
+ .suspend = usbio_suspend,
|
|
+ .resume = usbio_resume,
|
|
+ .id_table = usbio_table,
|
|
+ .supports_autosuspend = 1,
|
|
+};
|
|
+module_usb_driver(usbio_driver);
|
|
+
|
|
+struct usbio_match_ids_walk_data {
|
|
+ struct acpi_device *adev;
|
|
+ const struct acpi_device_id *hids;
|
|
+ unsigned int id;
|
|
+};
|
|
+
|
|
+static int usbio_match_device_ids(struct acpi_device *adev, void *data)
|
|
+{
|
|
+ struct usbio_match_ids_walk_data *wd = data;
|
|
+ unsigned int id = 0;
|
|
+ char *uid;
|
|
+
|
|
+ if (acpi_match_device_ids(adev, wd->hids))
|
|
+ return 0;
|
|
+
|
|
+ uid = acpi_device_uid(adev);
|
|
+ if (uid) {
|
|
+ for (int i = 0; i < strlen(uid); i++) {
|
|
+ if (!kstrtouint(&uid[i], 10, &id))
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!uid || wd->id == id) {
|
|
+ wd->adev = adev;
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void usbio_acpi_bind(struct auxiliary_device *adev, const struct acpi_device_id *hids)
|
|
+{
|
|
+ struct device *dev = &adev->dev;
|
|
+ struct acpi_device *parent;
|
|
+ struct usbio_match_ids_walk_data wd = {
|
|
+ .adev = NULL,
|
|
+ .hids = hids,
|
|
+ .id = adev->id,
|
|
+ };
|
|
+
|
|
+ parent = ACPI_COMPANION(dev->parent);
|
|
+ if (!parent)
|
|
+ return;
|
|
+
|
|
+ acpi_dev_for_each_child(parent, usbio_match_device_ids, &wd);
|
|
+ if (wd.adev)
|
|
+ ACPI_COMPANION_SET(dev, wd.adev);
|
|
+}
|
|
+EXPORT_SYMBOL_NS_GPL(usbio_acpi_bind, "USBIO");
|
|
+
|
|
+MODULE_DESCRIPTION("Intel USBIO Bridge driver");
|
|
+MODULE_AUTHOR("Israel Cepeda <israel.a.cepeda.lopez@intel.com>");
|
|
+MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
|
|
+MODULE_LICENSE("GPL");
|
|
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
|
|
index 5739ea2abdd1a..181351afe8877 100644
|
|
--- a/drivers/usb/typec/ucsi/ucsi.c
|
|
+++ b/drivers/usb/typec/ucsi/ucsi.c
|
|
@@ -1790,6 +1790,12 @@ static int ucsi_init(struct ucsi *ucsi)
|
|
ret = -ENODEV;
|
|
goto err_reset;
|
|
}
|
|
+ /* Check if reserved bit set. This is out of spec but happens in buggy FW */
|
|
+ if (ucsi->cap.num_connectors & 0x80) {
|
|
+ dev_warn(ucsi->dev, "UCSI: Invalid num_connectors %d. Likely buggy FW\n",
|
|
+ ucsi->cap.num_connectors);
|
|
+ ucsi->cap.num_connectors &= 0x7f; // clear bit and carry on
|
|
+ }
|
|
|
|
/* Allocate the connectors. Released in ucsi_unregister() */
|
|
connector = kcalloc(ucsi->cap.num_connectors + 1, sizeof(*connector), GFP_KERNEL);
|
|
diff --git a/include/linux/efi.h b/include/linux/efi.h
|
|
index a98cc39e7aaa8..00f31eefd0c5a 100644
|
|
--- a/include/linux/efi.h
|
|
+++ b/include/linux/efi.h
|
|
@@ -45,6 +45,8 @@ struct screen_info;
|
|
#define EFI_ABORTED (21 | (1UL << (BITS_PER_LONG-1)))
|
|
#define EFI_SECURITY_VIOLATION (26 | (1UL << (BITS_PER_LONG-1)))
|
|
|
|
+#define EFI_IS_ERROR(x) ((x) & (1UL << (BITS_PER_LONG-1)))
|
|
+
|
|
typedef unsigned long efi_status_t;
|
|
typedef u8 efi_bool_t;
|
|
typedef u16 efi_char16_t; /* UNICODE character */
|
|
@@ -865,6 +867,14 @@ static inline int efi_range_is_wc(unsigned long start, unsigned long len)
|
|
#define EFI_MEM_ATTR 9 /* Did firmware publish an EFI_MEMORY_ATTRIBUTES table? */
|
|
#define EFI_MEM_NO_SOFT_RESERVE 10 /* Is the kernel configured to ignore soft reservations? */
|
|
#define EFI_PRESERVE_BS_REGIONS 11 /* Are EFI boot-services memory segments available? */
|
|
+#define EFI_SECURE_BOOT 12 /* Are we in Secure Boot mode? */
|
|
+
|
|
+enum efi_secureboot_mode {
|
|
+ efi_secureboot_mode_unset,
|
|
+ efi_secureboot_mode_unknown,
|
|
+ efi_secureboot_mode_disabled,
|
|
+ efi_secureboot_mode_enabled,
|
|
+};
|
|
|
|
#ifdef CONFIG_EFI
|
|
/*
|
|
@@ -876,6 +886,8 @@ static inline bool efi_enabled(int feature)
|
|
}
|
|
extern void efi_reboot(enum reboot_mode reboot_mode, const char *__unused);
|
|
|
|
+extern void __init efi_set_secure_boot(enum efi_secureboot_mode mode);
|
|
+
|
|
bool __pure __efi_soft_reserve_enabled(void);
|
|
|
|
static inline bool __pure efi_soft_reserve_enabled(void)
|
|
@@ -897,6 +909,8 @@ static inline bool efi_enabled(int feature)
|
|
static inline void
|
|
efi_reboot(enum reboot_mode reboot_mode, const char *__unused) {}
|
|
|
|
+static inline void efi_set_secure_boot(enum efi_secureboot_mode mode) {}
|
|
+
|
|
static inline bool efi_soft_reserve_enabled(void)
|
|
{
|
|
return false;
|
|
@@ -911,6 +925,7 @@ static inline void efi_find_mirror(void) {}
|
|
#endif
|
|
|
|
extern int efi_status_to_err(efi_status_t status);
|
|
+extern const char *efi_status_to_str(efi_status_t status);
|
|
|
|
/*
|
|
* Variable Attributes
|
|
@@ -1126,13 +1141,6 @@ static inline bool efi_runtime_disabled(void) { return true; }
|
|
extern void efi_call_virt_check_flags(unsigned long flags, const void *caller);
|
|
extern unsigned long efi_call_virt_save_flags(void);
|
|
|
|
-enum efi_secureboot_mode {
|
|
- efi_secureboot_mode_unset,
|
|
- efi_secureboot_mode_unknown,
|
|
- efi_secureboot_mode_disabled,
|
|
- efi_secureboot_mode_enabled,
|
|
-};
|
|
-
|
|
static inline
|
|
enum efi_secureboot_mode efi_get_secureboot_mode(efi_get_variable_t *get_var)
|
|
{
|
|
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
|
|
index fd11fffdd3c38..af87dc6e56dd6 100644
|
|
--- a/include/linux/lsm_hook_defs.h
|
|
+++ b/include/linux/lsm_hook_defs.h
|
|
@@ -446,6 +446,7 @@ LSM_HOOK(int, 0, bpf_token_capable, const struct bpf_token *token, int cap)
|
|
|
|
LSM_HOOK(int, 0, locked_down, enum lockdown_reason what)
|
|
|
|
+
|
|
#ifdef CONFIG_PERF_EVENTS
|
|
LSM_HOOK(int, 0, perf_event_open, int type)
|
|
LSM_HOOK(int, 0, perf_event_alloc, struct perf_event *event)
|
|
diff --git a/include/linux/module.h b/include/linux/module.h
|
|
index 3319a5269d286..21e2c3a8c1e1d 100644
|
|
--- a/include/linux/module.h
|
|
+++ b/include/linux/module.h
|
|
@@ -410,6 +410,7 @@ struct module {
|
|
struct module_attribute *modinfo_attrs;
|
|
const char *version;
|
|
const char *srcversion;
|
|
+ const char *rhelversion;
|
|
struct kobject *holders_dir;
|
|
|
|
/* Exported symbols */
|
|
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
|
|
index ab7eea01ab427..fff7c5f737fc8 100644
|
|
--- a/include/linux/rmi.h
|
|
+++ b/include/linux/rmi.h
|
|
@@ -364,6 +364,7 @@ struct rmi_driver_data {
|
|
|
|
struct rmi4_attn_data attn_data;
|
|
DECLARE_KFIFO(attn_fifo, struct rmi4_attn_data, 16);
|
|
+ struct work_struct attn_work;
|
|
};
|
|
|
|
int rmi_register_transport_device(struct rmi_transport_dev *xport);
|
|
diff --git a/include/linux/security.h b/include/linux/security.h
|
|
index 521bcb5b97170..537e928730e98 100644
|
|
--- a/include/linux/security.h
|
|
+++ b/include/linux/security.h
|
|
@@ -2405,4 +2405,13 @@ static inline void security_initramfs_populated(void)
|
|
}
|
|
#endif /* CONFIG_SECURITY */
|
|
|
|
+#ifdef CONFIG_SECURITY_LOCKDOWN_LSM
|
|
+extern int security_lock_kernel_down(const char *where, enum lockdown_reason level);
|
|
+#else
|
|
+static inline int security_lock_kernel_down(const char *where, enum lockdown_reason level)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+#endif /* CONFIG_SECURITY_LOCKDOWN_LSM */
|
|
+
|
|
#endif /* ! __LINUX_SECURITY_H */
|
|
diff --git a/include/linux/usb/usbio.h b/include/linux/usb/usbio.h
|
|
new file mode 100644
|
|
index 0000000000000..6c4e7c246d580
|
|
--- /dev/null
|
|
+++ b/include/linux/usb/usbio.h
|
|
@@ -0,0 +1,177 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0 */
|
|
+/*
|
|
+ * Copyright (c) 2025 Intel Corporation.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#ifndef _LINUX_USBIO_H_
|
|
+#define _LINUX_USBIO_H_
|
|
+
|
|
+#include <linux/auxiliary_bus.h>
|
|
+#include <linux/byteorder/generic.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/types.h>
|
|
+
|
|
+/***********************
|
|
+ * USBIO Clients Names *
|
|
+ ***********************/
|
|
+#define USBIO_GPIO_CLIENT "usbio-gpio"
|
|
+#define USBIO_I2C_CLIENT "usbio-i2c"
|
|
+
|
|
+/****************
|
|
+ * USBIO quirks *
|
|
+ ****************/
|
|
+#define USBIO_QUIRK_BULK_MAXP_63 BIT(0) /* Force bulk endpoint maxp to 63 */
|
|
+#define USBIO_QUIRK_I2C_NO_INIT_ACK BIT(8) /* Do not ask for ack on I2C init */
|
|
+#define USBIO_QUIRK_I2C_MAX_RW_LEN_52 BIT(9) /* Set i2c-adapter max r/w len to 52 */
|
|
+#define USBIO_QUIRK_I2C_USE_CHUNK_LEN BIT(10) /* Send chunk-len for split xfers */
|
|
+#define USBIO_QUIRK_I2C_ALLOW_400KHZ BIT(11) /* Override desc, allowing 400 KHz */
|
|
+
|
|
+/**************************
|
|
+ * USBIO Type Definitions *
|
|
+ **************************/
|
|
+
|
|
+/* USBIO Packet Type */
|
|
+#define USBIO_PKTTYPE_CTRL 1
|
|
+#define USBIO_PKTTYPE_DBG 2
|
|
+#define USBIO_PKTTYPE_GPIO 3
|
|
+#define USBIO_PKTTYPE_I2C 4
|
|
+
|
|
+/* USBIO Packet Header */
|
|
+struct usbio_packet_header {
|
|
+ u8 type;
|
|
+ u8 cmd;
|
|
+ u8 flags;
|
|
+} __packed;
|
|
+
|
|
+/* USBIO Control Transfer Packet */
|
|
+struct usbio_ctrl_packet {
|
|
+ struct usbio_packet_header header;
|
|
+ u8 len;
|
|
+ u8 data[] __counted_by(len);
|
|
+} __packed;
|
|
+
|
|
+/* USBIO Bulk Transfer Packet */
|
|
+struct usbio_bulk_packet {
|
|
+ struct usbio_packet_header header;
|
|
+ __le16 len;
|
|
+ u8 data[] __counted_by(len);
|
|
+} __packed;
|
|
+
|
|
+/* USBIO GPIO commands */
|
|
+enum usbio_gpio_cmd {
|
|
+ USBIO_GPIOCMD_DEINIT,
|
|
+ USBIO_GPIOCMD_INIT,
|
|
+ USBIO_GPIOCMD_READ,
|
|
+ USBIO_GPIOCMD_WRITE,
|
|
+ USBIO_GPIOCMD_END
|
|
+};
|
|
+
|
|
+/* USBIO GPIO config */
|
|
+enum usbio_gpio_pincfg {
|
|
+ USBIO_GPIO_PINCFG_DEFAULT,
|
|
+ USBIO_GPIO_PINCFG_PULLUP,
|
|
+ USBIO_GPIO_PINCFG_PULLDOWN,
|
|
+ USBIO_GPIO_PINCFG_PUSHPULL
|
|
+};
|
|
+
|
|
+#define USBIO_GPIO_PINCFG_SHIFT 2
|
|
+#define USBIO_GPIO_PINCFG_MASK (0x3 << USBIO_GPIO_PINCFG_SHIFT)
|
|
+#define USBIO_GPIO_SET_PINCFG(pincfg) \
|
|
+ (((pincfg) << USBIO_GPIO_PINCFG_SHIFT) & USBIO_GPIO_PINCFG_MASK)
|
|
+
|
|
+enum usbio_gpio_pinmode {
|
|
+ USBIO_GPIO_PINMOD_INVAL,
|
|
+ USBIO_GPIO_PINMOD_INPUT,
|
|
+ USBIO_GPIO_PINMOD_OUTPUT,
|
|
+ USBIO_GPIO_PINMOD_MAXVAL
|
|
+};
|
|
+
|
|
+#define USBIO_GPIO_PINMOD_MASK 0x3
|
|
+#define USBIO_GPIO_SET_PINMOD(pin) (pin & USBIO_GPIO_PINMOD_MASK)
|
|
+
|
|
+/*************************
|
|
+ * USBIO GPIO Controller *
|
|
+ *************************/
|
|
+
|
|
+#define USBIO_MAX_GPIOBANKS 5
|
|
+#define USBIO_GPIOSPERBANK 32
|
|
+
|
|
+struct usbio_gpio_bank_desc {
|
|
+ u8 id;
|
|
+ u8 pins;
|
|
+ __le32 bmap;
|
|
+} __packed;
|
|
+
|
|
+struct usbio_gpio_init {
|
|
+ u8 bankid;
|
|
+ u8 config;
|
|
+ u8 pincount;
|
|
+ u8 pin;
|
|
+} __packed;
|
|
+
|
|
+struct usbio_gpio_rw {
|
|
+ u8 bankid;
|
|
+ u8 pincount;
|
|
+ u8 pin;
|
|
+ __le32 value;
|
|
+} __packed;
|
|
+
|
|
+/* USBIO I2C commands */
|
|
+enum usbio_i2c_cmd {
|
|
+ USBIO_I2CCMD_UNINIT,
|
|
+ USBIO_I2CCMD_INIT,
|
|
+ USBIO_I2CCMD_READ,
|
|
+ USBIO_I2CCMD_WRITE,
|
|
+ USBIO_I2CCMD_END
|
|
+};
|
|
+
|
|
+/************************
|
|
+ * USBIO I2C Controller *
|
|
+ ************************/
|
|
+
|
|
+#define USBIO_MAX_I2CBUSES 5
|
|
+
|
|
+#define USBIO_I2C_BUS_ADDR_CAP_10B BIT(3) /* 10bit address support */
|
|
+#define USBIO_I2C_BUS_MODE_CAP_MASK 0x3
|
|
+#define USBIO_I2C_BUS_MODE_CAP_SM 0 /* Standard Mode */
|
|
+#define USBIO_I2C_BUS_MODE_CAP_FM 1 /* Fast Mode */
|
|
+#define USBIO_I2C_BUS_MODE_CAP_FMP 2 /* Fast Mode+ */
|
|
+#define USBIO_I2C_BUS_MODE_CAP_HSM 3 /* High-Speed Mode */
|
|
+
|
|
+struct usbio_i2c_bus_desc {
|
|
+ u8 id;
|
|
+ u8 caps;
|
|
+} __packed;
|
|
+
|
|
+struct usbio_i2c_uninit {
|
|
+ u8 busid;
|
|
+ __le16 config;
|
|
+} __packed;
|
|
+
|
|
+struct usbio_i2c_init {
|
|
+ u8 busid;
|
|
+ __le16 config;
|
|
+ __le32 speed;
|
|
+} __packed;
|
|
+
|
|
+struct usbio_i2c_rw {
|
|
+ u8 busid;
|
|
+ __le16 config;
|
|
+ __le16 size;
|
|
+ u8 data[] __counted_by(size);
|
|
+} __packed;
|
|
+
|
|
+int usbio_control_msg(struct auxiliary_device *adev, u8 type, u8 cmd,
|
|
+ const void *obuf, u16 obuf_len, void *ibuf, u16 ibuf_len);
|
|
+
|
|
+int usbio_bulk_msg(struct auxiliary_device *adev, u8 type, u8 cmd, bool last,
|
|
+ const void *obuf, u16 obuf_len, void *ibuf, u16 ibuf_len);
|
|
+
|
|
+int usbio_acquire(struct auxiliary_device *adev);
|
|
+void usbio_release(struct auxiliary_device *adev);
|
|
+void usbio_get_txrxbuf_len(struct auxiliary_device *adev, u16 *txbuf_len, u16 *rxbuf_len);
|
|
+unsigned long usbio_get_quirks(struct auxiliary_device *adev);
|
|
+void usbio_acpi_bind(struct auxiliary_device *adev, const struct acpi_device_id *hids);
|
|
+
|
|
+#endif
|
|
diff --git a/kernel/module/main.c b/kernel/module/main.c
|
|
index c66b261849362..7da1349a42a27 100644
|
|
--- a/kernel/module/main.c
|
|
+++ b/kernel/module/main.c
|
|
@@ -606,6 +606,7 @@ static const struct module_attribute modinfo_##field = { \
|
|
|
|
MODINFO_ATTR(version);
|
|
MODINFO_ATTR(srcversion);
|
|
+MODINFO_ATTR(rhelversion);
|
|
|
|
static struct {
|
|
char name[MODULE_NAME_LEN];
|
|
@@ -1058,6 +1059,7 @@ const struct module_attribute *const modinfo_attrs[] = {
|
|
&module_uevent,
|
|
&modinfo_version,
|
|
&modinfo_srcversion,
|
|
+ &modinfo_rhelversion,
|
|
&modinfo_initstate,
|
|
&modinfo_coresize,
|
|
#ifdef CONFIG_ARCH_WANTS_MODULES_DATA_IN_VMALLOC
|
|
diff --git a/kernel/module/signing.c b/kernel/module/signing.c
|
|
index a2ff4242e623d..f0d2be1ee4f1c 100644
|
|
--- a/kernel/module/signing.c
|
|
+++ b/kernel/module/signing.c
|
|
@@ -61,10 +61,17 @@ int mod_verify_sig(const void *mod, struct load_info *info)
|
|
modlen -= sig_len + sizeof(ms);
|
|
info->len = modlen;
|
|
|
|
- return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
|
|
+ ret = verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
|
|
VERIFY_USE_SECONDARY_KEYRING,
|
|
VERIFYING_MODULE_SIGNATURE,
|
|
NULL, NULL);
|
|
+ if (ret == -ENOKEY && IS_ENABLED(CONFIG_INTEGRITY_PLATFORM_KEYRING)) {
|
|
+ ret = verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
|
|
+ VERIFY_USE_PLATFORM_KEYRING,
|
|
+ VERIFYING_MODULE_SIGNATURE,
|
|
+ NULL, NULL);
|
|
+ }
|
|
+ return ret;
|
|
}
|
|
|
|
int module_sig_check(struct load_info *info, int flags)
|
|
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
|
|
index 1d581ba5df66f..7826803444df9 100644
|
|
--- a/scripts/Makefile.lib
|
|
+++ b/scripts/Makefile.lib
|
|
@@ -191,7 +191,10 @@ objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call
|
|
objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess
|
|
objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable
|
|
objtool-args-$(CONFIG_PREFIX_SYMBOLS) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
|
|
+# RHEL-only: don't enforce OBJTOOL_WERROR for out of tree modules
|
|
+ifeq ($(KBUILD_EXTMOD),)
|
|
objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror
|
|
+endif
|
|
|
|
objtool-args = $(objtool-args-y) \
|
|
$(if $(delay-objtool), --link) \
|
|
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
|
|
index 5ca7c268294eb..c7e3c64bc8034 100644
|
|
--- a/scripts/mod/modpost.c
|
|
+++ b/scripts/mod/modpost.c
|
|
@@ -27,6 +27,7 @@
|
|
#include <xalloc.h>
|
|
#include "modpost.h"
|
|
#include "../../include/linux/license.h"
|
|
+#include "../../include/generated/uapi/linux/version.h"
|
|
|
|
#define MODULE_NS_PREFIX "module:"
|
|
|
|
@@ -2029,6 +2030,12 @@ static void write_buf(struct buffer *b, const char *fname)
|
|
}
|
|
}
|
|
|
|
+static void add_rhelversion(struct buffer *b, struct module *mod)
|
|
+{
|
|
+ buf_printf(b, "MODULE_INFO(rhelversion, \"%d.%d\");\n", RHEL_MAJOR,
|
|
+ RHEL_MINOR);
|
|
+}
|
|
+
|
|
static void write_if_changed(struct buffer *b, const char *fname)
|
|
{
|
|
char *tmp;
|
|
@@ -2098,6 +2105,7 @@ static void write_mod_c_file(struct module *mod)
|
|
}
|
|
|
|
add_srcversion(&buf, mod);
|
|
+ add_rhelversion(&buf, mod);
|
|
|
|
ret = snprintf(fname, sizeof(fname), "%s.mod.c", mod->name);
|
|
if (ret >= sizeof(fname)) {
|
|
diff --git a/scripts/tags.sh b/scripts/tags.sh
|
|
index 99ce427d9a69d..f191cd9d7ee6e 100755
|
|
--- a/scripts/tags.sh
|
|
+++ b/scripts/tags.sh
|
|
@@ -16,6 +16,8 @@ fi
|
|
ignore="$(echo "$RCS_FIND_IGNORE" | sed 's|\\||g' )"
|
|
# tags and cscope files should also ignore MODVERSION *.mod.c files
|
|
ignore="$ignore ( -name *.mod.c ) -prune -o"
|
|
+# RHEL tags and cscope should also ignore redhat/rpm
|
|
+ignore="$ignore ( -path redhat/rpm ) -prune -o"
|
|
|
|
# ignore arbitrary directories
|
|
if [ -n "${IGNORE_DIRS}" ]; then
|
|
diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c
|
|
index d1fdd113450a6..182e8090cfe85 100644
|
|
--- a/security/integrity/platform_certs/load_uefi.c
|
|
+++ b/security/integrity/platform_certs/load_uefi.c
|
|
@@ -74,7 +74,8 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid,
|
|
return NULL;
|
|
|
|
if (*status != EFI_BUFFER_TOO_SMALL) {
|
|
- pr_err("Couldn't get size: 0x%lx\n", *status);
|
|
+ pr_err("Couldn't get size: %s (0x%lx)\n",
|
|
+ efi_status_to_str(*status), *status);
|
|
return NULL;
|
|
}
|
|
|
|
@@ -85,7 +86,8 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid,
|
|
*status = efi.get_variable(name, guid, NULL, &lsize, db);
|
|
if (*status != EFI_SUCCESS) {
|
|
kfree(db);
|
|
- pr_err("Error reading db var: 0x%lx\n", *status);
|
|
+ pr_err("Error reading db var: %s (0x%lx)\n",
|
|
+ efi_status_to_str(*status), *status);
|
|
return NULL;
|
|
}
|
|
|
|
diff --git a/security/lockdown/Kconfig b/security/lockdown/Kconfig
|
|
index e84ddf4840101..d0501353a4b95 100644
|
|
--- a/security/lockdown/Kconfig
|
|
+++ b/security/lockdown/Kconfig
|
|
@@ -16,6 +16,19 @@ config SECURITY_LOCKDOWN_LSM_EARLY
|
|
subsystem is fully initialised. If enabled, lockdown will
|
|
unconditionally be called before any other LSMs.
|
|
|
|
+config LOCK_DOWN_IN_EFI_SECURE_BOOT
|
|
+ bool "Lock down the kernel in EFI Secure Boot mode"
|
|
+ default n
|
|
+ depends on EFI && SECURITY_LOCKDOWN_LSM_EARLY
|
|
+ help
|
|
+ UEFI Secure Boot provides a mechanism for ensuring that the firmware
|
|
+ will only load signed bootloaders and kernels. Secure boot mode may
|
|
+ be determined from EFI variables provided by the system firmware if
|
|
+ not indicated by the boot parameters.
|
|
+
|
|
+ Enabling this option results in kernel lockdown being triggered if
|
|
+ EFI Secure Boot is set.
|
|
+
|
|
choice
|
|
prompt "Kernel default lockdown mode"
|
|
default LOCK_DOWN_KERNEL_FORCE_NONE
|
|
diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
|
|
index cf83afa1d879a..aba751e7abffe 100644
|
|
--- a/security/lockdown/lockdown.c
|
|
+++ b/security/lockdown/lockdown.c
|
|
@@ -72,6 +72,17 @@ static int lockdown_is_locked_down(enum lockdown_reason what)
|
|
return 0;
|
|
}
|
|
|
|
+/**
|
|
+ * security_lock_kernel_down() - Put the kernel into lock-down mode.
|
|
+ *
|
|
+ * @where: Where the lock-down is originating from (e.g. command line option)
|
|
+ * @level: The lock-down level (can only increase)
|
|
+ */
|
|
+int security_lock_kernel_down(const char *where, enum lockdown_reason level)
|
|
+{
|
|
+ return lock_kernel_down(where, level);
|
|
+}
|
|
+
|
|
static struct security_hook_list lockdown_hooks[] __ro_after_init = {
|
|
LSM_HOOK_INIT(locked_down, lockdown_is_locked_down),
|
|
};
|
|
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
|
|
index fd6b370c81698..225544b1f9f64 100644
|
|
--- a/tools/testing/selftests/bpf/Makefile
|
|
+++ b/tools/testing/selftests/bpf/Makefile
|
|
@@ -502,7 +502,7 @@ LSKELS := fentry_test.c fexit_test.c fexit_sleep.c atomics.c \
|
|
test_ringbuf_n.c test_ringbuf_map_key.c test_ringbuf_write.c
|
|
|
|
# Generate both light skeleton and libbpf skeleton for these
|
|
-LSKELS_EXTRA := test_ksyms_module.c test_ksyms_weak.c kfunc_call_test.c \
|
|
+LSKELS_EXTRA := test_ksyms_module.c kfunc_call_test.c \
|
|
kfunc_call_test_subprog.c
|
|
SKEL_BLACKLIST += $$(LSKELS)
|
|
|
|
diff --git a/tools/testing/selftests/bpf/prog_tests/ksyms_btf.c b/tools/testing/selftests/bpf/prog_tests/ksyms_btf.c
|
|
index 1d7a2f1e07317..b22f3a9cb8b80 100644
|
|
--- a/tools/testing/selftests/bpf/prog_tests/ksyms_btf.c
|
|
+++ b/tools/testing/selftests/bpf/prog_tests/ksyms_btf.c
|
|
@@ -7,7 +7,6 @@
|
|
#include "test_ksyms_btf.skel.h"
|
|
#include "test_ksyms_btf_null_check.skel.h"
|
|
#include "test_ksyms_weak.skel.h"
|
|
-#include "test_ksyms_weak.lskel.h"
|
|
#include "test_ksyms_btf_write_check.skel.h"
|
|
|
|
static int duration;
|
|
@@ -111,33 +110,6 @@ static void test_weak_syms(void)
|
|
test_ksyms_weak__destroy(skel);
|
|
}
|
|
|
|
-static void test_weak_syms_lskel(void)
|
|
-{
|
|
- struct test_ksyms_weak_lskel *skel;
|
|
- struct test_ksyms_weak_lskel__data *data;
|
|
- int err;
|
|
-
|
|
- skel = test_ksyms_weak_lskel__open_and_load();
|
|
- if (!ASSERT_OK_PTR(skel, "test_ksyms_weak_lskel__open_and_load"))
|
|
- return;
|
|
-
|
|
- err = test_ksyms_weak_lskel__attach(skel);
|
|
- if (!ASSERT_OK(err, "test_ksyms_weak_lskel__attach"))
|
|
- goto cleanup;
|
|
-
|
|
- /* trigger tracepoint */
|
|
- usleep(1);
|
|
-
|
|
- data = skel->data;
|
|
- ASSERT_EQ(data->out__existing_typed, 0, "existing typed ksym");
|
|
- ASSERT_NEQ(data->out__existing_typeless, -1, "existing typeless ksym");
|
|
- ASSERT_EQ(data->out__non_existent_typeless, 0, "nonexistent typeless ksym");
|
|
- ASSERT_EQ(data->out__non_existent_typed, 0, "nonexistent typed ksym");
|
|
-
|
|
-cleanup:
|
|
- test_ksyms_weak_lskel__destroy(skel);
|
|
-}
|
|
-
|
|
static void test_write_check(bool test_handler1)
|
|
{
|
|
struct test_ksyms_btf_write_check *skel;
|
|
@@ -180,9 +152,6 @@ void test_ksyms_btf(void)
|
|
if (test__start_subtest("weak_ksyms"))
|
|
test_weak_syms();
|
|
|
|
- if (test__start_subtest("weak_ksyms_lskel"))
|
|
- test_weak_syms_lskel();
|
|
-
|
|
if (test__start_subtest("write_check1"))
|
|
test_write_check(true);
|
|
|