diff --git a/0001-drivers-crypto-nx-fix-init-race-alignmasks-and-GCM-b.patch b/0001-drivers-crypto-nx-fix-init-race-alignmasks-and-GCM-b.patch deleted file mode 100644 index c8d30455e..000000000 --- a/0001-drivers-crypto-nx-fix-init-race-alignmasks-and-GCM-b.patch +++ /dev/null @@ -1,83 +0,0 @@ -From b05ceba560e094d27ff716f6df1e2d5ef670d4d3 Mon Sep 17 00:00:00 2001 -From: Kent Yoder -Date: Wed, 27 Feb 2013 15:50:27 -0600 -Subject: [PATCH] drivers/crypto/nx: fix init race, alignmasks and GCM bug - - Fixes a race on driver init with registering algorithms where the -driver status flag wasn't being set before self testing started. - - Added the cra_alignmask field for CBC and ECB modes. - - Fixed a bug in GCM where AES block size was being used instead of -authsize. - -Signed-off-by: Kent Yoder ---- - drivers/crypto/nx/nx-aes-cbc.c | 1 + - drivers/crypto/nx/nx-aes-ecb.c | 1 + - drivers/crypto/nx/nx-aes-gcm.c | 2 +- - drivers/crypto/nx/nx.c | 4 ++-- - 4 files changed, 5 insertions(+), 3 deletions(-) - -diff --git a/drivers/crypto/nx/nx-aes-cbc.c b/drivers/crypto/nx/nx-aes-cbc.c -index a76d4c4..35d483f 100644 ---- a/drivers/crypto/nx/nx-aes-cbc.c -+++ b/drivers/crypto/nx/nx-aes-cbc.c -@@ -126,6 +126,7 @@ struct crypto_alg nx_cbc_aes_alg = { - .cra_blocksize = AES_BLOCK_SIZE, - .cra_ctxsize = sizeof(struct nx_crypto_ctx), - .cra_type = &crypto_blkcipher_type, -+ .cra_alignmask = 0xf, - .cra_module = THIS_MODULE, - .cra_init = nx_crypto_ctx_aes_cbc_init, - .cra_exit = nx_crypto_ctx_exit, -diff --git a/drivers/crypto/nx/nx-aes-ecb.c b/drivers/crypto/nx/nx-aes-ecb.c -index ba5f161..7bbc9a8 100644 ---- a/drivers/crypto/nx/nx-aes-ecb.c -+++ b/drivers/crypto/nx/nx-aes-ecb.c -@@ -123,6 +123,7 @@ struct crypto_alg nx_ecb_aes_alg = { - .cra_priority = 300, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, - .cra_blocksize = AES_BLOCK_SIZE, -+ .cra_alignmask = 0xf, - .cra_ctxsize = sizeof(struct nx_crypto_ctx), - .cra_type = &crypto_blkcipher_type, - .cra_module = THIS_MODULE, -diff --git a/drivers/crypto/nx/nx-aes-gcm.c b/drivers/crypto/nx/nx-aes-gcm.c -index c8109ed..6cca6c3 100644 ---- a/drivers/crypto/nx/nx-aes-gcm.c -+++ b/drivers/crypto/nx/nx-aes-gcm.c -@@ -219,7 +219,7 @@ static int gcm_aes_nx_crypt(struct aead_request *req, int enc) - if (enc) - NX_CPB_FDM(csbcpb) |= NX_FDM_ENDE_ENCRYPT; - else -- nbytes -= AES_BLOCK_SIZE; -+ nbytes -= crypto_aead_authsize(crypto_aead_reqtfm(req)); - - csbcpb->cpb.aes_gcm.bit_length_data = nbytes * 8; - -diff --git a/drivers/crypto/nx/nx.c b/drivers/crypto/nx/nx.c -index c767f23..7621d05 100644 ---- a/drivers/crypto/nx/nx.c -+++ b/drivers/crypto/nx/nx.c -@@ -454,6 +454,8 @@ static int nx_register_algs(void) - if (rc) - goto out; - -+ nx_driver.of.status = NX_OKAY; -+ - rc = crypto_register_alg(&nx_ecb_aes_alg); - if (rc) - goto out; -@@ -498,8 +500,6 @@ static int nx_register_algs(void) - if (rc) - goto out_unreg_s512; - -- nx_driver.of.status = NX_OKAY; -- - goto out; - - out_unreg_s512: --- -1.7.11.7 - diff --git a/VMX-x86-handle-host-TSC-calibration-failure.patch b/VMX-x86-handle-host-TSC-calibration-failure.patch deleted file mode 100644 index 6b6ddd2d2..000000000 --- a/VMX-x86-handle-host-TSC-calibration-failure.patch +++ /dev/null @@ -1,58 +0,0 @@ -@@ -, +, @@ - VMX: x86: handle host TSC calibration failure - - If the host TSC calibration fails, tsc_khz is zero (see tsc_init.c). - Handle such case properly in KVM (instead of dividing by zero). - - https://bugzilla.redhat.com/show_bug.cgi?id=859282 - - Signed-off-by: Marcelo Tosatti - Signed-off-by: Gleb Natapov ---- a/arch/x86/kvm/x86.c -+++ a/arch/x86/kvm/x86.c -@@ -1079,6 +1079,10 @@ static void kvm_set_tsc_khz(struct kvm_vcpu *vcpu, u32 this_tsc_khz) - u32 thresh_lo, thresh_hi; - int use_scaling = 0; - -+ /* tsc_khz can be zero if TSC calibration fails */ -+ if (this_tsc_khz == 0) -+ return; -+ - /* Compute a scale to convert nanoseconds in TSC cycles */ - kvm_get_time_scale(this_tsc_khz, NSEC_PER_SEC / 1000, - &vcpu->arch.virtual_tsc_shift, -@@ -1156,20 +1160,23 @@ void kvm_write_tsc(struct kvm_vcpu *vcpu, struct msr_data *msr) - ns = get_kernel_ns(); - elapsed = ns - kvm->arch.last_tsc_nsec; - -- /* n.b - signed multiplication and division required */ -- usdiff = data - kvm->arch.last_tsc_write; -+ if (vcpu->arch.virtual_tsc_khz) { -+ /* n.b - signed multiplication and division required */ -+ usdiff = data - kvm->arch.last_tsc_write; - #ifdef CONFIG_X86_64 -- usdiff = (usdiff * 1000) / vcpu->arch.virtual_tsc_khz; -+ usdiff = (usdiff * 1000) / vcpu->arch.virtual_tsc_khz; - #else -- /* do_div() only does unsigned */ -- asm("idivl %2; xor %%edx, %%edx" -- : "=A"(usdiff) -- : "A"(usdiff * 1000), "rm"(vcpu->arch.virtual_tsc_khz)); -+ /* do_div() only does unsigned */ -+ asm("idivl %2; xor %%edx, %%edx" -+ : "=A"(usdiff) -+ : "A"(usdiff * 1000), "rm"(vcpu->arch.virtual_tsc_khz)); - #endif -- do_div(elapsed, 1000); -- usdiff -= elapsed; -- if (usdiff < 0) -- usdiff = -usdiff; -+ do_div(elapsed, 1000); -+ usdiff -= elapsed; -+ if (usdiff < 0) -+ usdiff = -usdiff; -+ } else -+ usdiff = USEC_PER_SEC; /* disable TSC match window below */ - - /* - * Special case: TSC write with a small delta (1 second) of virtual diff --git a/af_key-fix-info-leaks-in-notify-messages.patch b/af_key-fix-info-leaks-in-notify-messages.patch deleted file mode 100644 index 9d20aec0f..000000000 --- a/af_key-fix-info-leaks-in-notify-messages.patch +++ /dev/null @@ -1,37 +0,0 @@ -From a5cc68f3d63306d0d288f31edfc2ae6ef8ecd887 Mon Sep 17 00:00:00 2001 -From: Mathias Krause -Date: Wed, 26 Jun 2013 21:52:30 +0000 -Subject: af_key: fix info leaks in notify messages - -key_notify_sa_flush() and key_notify_policy_flush() miss to initialize -the sadb_msg_reserved member of the broadcasted message and thereby -leak 2 bytes of heap memory to listeners. Fix that. - -Signed-off-by: Mathias Krause -Cc: Steffen Klassert -Cc: "David S. Miller" -Cc: Herbert Xu -Signed-off-by: David S. Miller ---- -diff --git a/net/key/af_key.c b/net/key/af_key.c -index c5fbd75..9da8620 100644 ---- a/net/key/af_key.c -+++ b/net/key/af_key.c -@@ -1710,6 +1710,7 @@ static int key_notify_sa_flush(const struct km_event *c) - hdr->sadb_msg_version = PF_KEY_V2; - hdr->sadb_msg_errno = (uint8_t) 0; - hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t)); -+ hdr->sadb_msg_reserved = 0; - - pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net); - -@@ -2699,6 +2700,7 @@ static int key_notify_policy_flush(const struct km_event *c) - hdr->sadb_msg_errno = (uint8_t) 0; - hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC; - hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t)); -+ hdr->sadb_msg_reserved = 0; - pfkey_broadcast(skb_out, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net); - return 0; - --- -cgit v0.9.2 diff --git a/arm-omap-ehci-fix.patch b/arm-omap-ehci-fix.patch deleted file mode 100644 index f6fc0a934..000000000 --- a/arm-omap-ehci-fix.patch +++ /dev/null @@ -1,190 +0,0 @@ -From 54a419668b0f27b7982807fb2376d237e0a0ce05 Mon Sep 17 00:00:00 2001 -From: Alan Stern -Date: Tue, 12 Mar 2013 10:44:39 +0000 -Subject: USB: EHCI: split ehci-omap out to a separate driver - -This patch (as1645) converts ehci-omap over to the new "ehci-hcd is a -library" approach, so that it can coexist peacefully with other EHCI -platform drivers and can make use of the private area allocated at -the end of struct ehci_hcd. - -Signed-off-by: Alan Stern -Signed-off-by: Greg Kroah-Hartman ---- -diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig -index c59a112..62f4e9a 100644 ---- a/drivers/usb/host/Kconfig -+++ b/drivers/usb/host/Kconfig -@@ -155,7 +155,7 @@ config USB_EHCI_MXC - Variation of ARC USB block used in some Freescale chips. - - config USB_EHCI_HCD_OMAP -- bool "EHCI support for OMAP3 and later chips" -+ tristate "EHCI support for OMAP3 and later chips" - depends on USB_EHCI_HCD && ARCH_OMAP - default y - ---help--- -diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile -index 001fbff..56de410 100644 ---- a/drivers/usb/host/Makefile -+++ b/drivers/usb/host/Makefile -@@ -27,6 +27,7 @@ obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o - obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o - obj-$(CONFIG_USB_EHCI_HCD_PLATFORM) += ehci-platform.o - obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o -+obj-$(CONFIG_USB_EHCI_HCD_OMAP) += ehci-omap.o - - obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o - obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o -diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c -index b416a3f..303b022 100644 ---- a/drivers/usb/host/ehci-hcd.c -+++ b/drivers/usb/host/ehci-hcd.c -@@ -1252,11 +1252,6 @@ MODULE_LICENSE ("GPL"); - #define PLATFORM_DRIVER ehci_hcd_sh_driver - #endif - --#ifdef CONFIG_USB_EHCI_HCD_OMAP --#include "ehci-omap.c" --#define PLATFORM_DRIVER ehci_hcd_omap_driver --#endif -- - #ifdef CONFIG_PPC_PS3 - #include "ehci-ps3.c" - #define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver -@@ -1346,6 +1341,7 @@ MODULE_LICENSE ("GPL"); - !IS_ENABLED(CONFIG_USB_EHCI_HCD_PLATFORM) && \ - !IS_ENABLED(CONFIG_USB_CHIPIDEA_HOST) && \ - !IS_ENABLED(CONFIG_USB_EHCI_MXC) && \ -+ !IS_ENABLED(CONFIG_USB_EHCI_HCD_OMAP) && \ - !defined(PLATFORM_DRIVER) && \ - !defined(PS3_SYSTEM_BUS_DRIVER) && \ - !defined(OF_PLATFORM_DRIVER) && \ -diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c -index 0555ee4..fa66757 100644 ---- a/drivers/usb/host/ehci-omap.c -+++ b/drivers/usb/host/ehci-omap.c -@@ -36,6 +36,9 @@ - * - convert to use hwmod and runtime PM - */ - -+#include -+#include -+#include - #include - #include - #include -@@ -43,6 +46,10 @@ - #include - #include - #include -+#include -+#include -+ -+#include "ehci.h" - - #include - -@@ -57,9 +64,11 @@ - #define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8 - #define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0 - --/*-------------------------------------------------------------------------*/ -+#define DRIVER_DESC "OMAP-EHCI Host Controller driver" - --static const struct hc_driver ehci_omap_hc_driver; -+static const char hcd_name[] = "ehci-omap"; -+ -+/*-------------------------------------------------------------------------*/ - - - static inline void ehci_write(void __iomem *base, u32 reg, u32 val) -@@ -166,6 +175,12 @@ static void disable_put_regulator( - /* configure so an HC device and id are always provided */ - /* always called with process context; sleeping is OK */ - -+static struct hc_driver __read_mostly ehci_omap_hc_driver; -+ -+static const struct ehci_driver_overrides ehci_omap_overrides __initdata = { -+ .reset = omap_ehci_init, -+}; -+ - /** - * ehci_hcd_omap_probe - initialize TI-based HCDs - * -@@ -315,56 +330,33 @@ static struct platform_driver ehci_hcd_omap_driver = { - /*.suspend = ehci_hcd_omap_suspend, */ - /*.resume = ehci_hcd_omap_resume, */ - .driver = { -- .name = "ehci-omap", -+ .name = hcd_name, - } - }; - - /*-------------------------------------------------------------------------*/ - --static const struct hc_driver ehci_omap_hc_driver = { -- .description = hcd_name, -- .product_desc = "OMAP-EHCI Host Controller", -- .hcd_priv_size = sizeof(struct ehci_hcd), -- -- /* -- * generic hardware linkage -- */ -- .irq = ehci_irq, -- .flags = HCD_MEMORY | HCD_USB2, -- -- /* -- * basic lifecycle operations -- */ -- .reset = omap_ehci_init, -- .start = ehci_run, -- .stop = ehci_stop, -- .shutdown = ehci_shutdown, -- -- /* -- * managing i/o requests and associated device resources -- */ -- .urb_enqueue = ehci_urb_enqueue, -- .urb_dequeue = ehci_urb_dequeue, -- .endpoint_disable = ehci_endpoint_disable, -- .endpoint_reset = ehci_endpoint_reset, -+static int __init ehci_omap_init(void) -+{ -+ if (usb_disabled()) -+ return -ENODEV; - -- /* -- * scheduling support -- */ -- .get_frame_number = ehci_get_frame, -+ pr_info("%s: " DRIVER_DESC "\n", hcd_name); - -- /* -- * root hub support -- */ -- .hub_status_data = ehci_hub_status_data, -- .hub_control = ehci_hub_control, -- .bus_suspend = ehci_bus_suspend, -- .bus_resume = ehci_bus_resume, -+ ehci_init_driver(&ehci_omap_hc_driver, &ehci_omap_overrides); -+ return platform_driver_register(&ehci_hcd_omap_driver); -+} -+module_init(ehci_omap_init); - -- .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, --}; -+static void __exit ehci_omap_cleanup(void) -+{ -+ platform_driver_unregister(&ehci_hcd_omap_driver); -+} -+module_exit(ehci_omap_cleanup); - - MODULE_ALIAS("platform:ehci-omap"); - MODULE_AUTHOR("Texas Instruments, Inc."); - MODULE_AUTHOR("Felipe Balbi "); - -+MODULE_DESCRIPTION(DRIVER_DESC); -+MODULE_LICENSE("GPL"); --- -cgit v0.9.1 diff --git a/arm-tegra-fixclk.patch b/arm-tegra-fixclk.patch deleted file mode 100644 index df0991293..000000000 --- a/arm-tegra-fixclk.patch +++ /dev/null @@ -1,28 +0,0 @@ -diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c -index 788486e..2f4d0e3 100644 ---- a/drivers/clk/tegra/clk-periph.c -+++ b/drivers/clk/tegra/clk-periph.c -@@ -18,6 +18,7 @@ - #include - #include - #include -+#include - - #include "clk.h" - -@@ -128,6 +129,7 @@ void tegra_periph_reset_deassert(struct clk *c) - - tegra_periph_reset(gate, 0); - } -+EXPORT_SYMBOL_GPL(tegra_periph_reset_deassert); - - void tegra_periph_reset_assert(struct clk *c) - { -@@ -147,6 +149,7 @@ void tegra_periph_reset_assert(struct clk *c) - - tegra_periph_reset(gate, 1); - } -+EXPORT_SYMBOL_GPL(tegra_periph_reset_assert); - - const struct clk_ops tegra_clk_periph_ops = { - .get_parent = clk_periph_get_parent, diff --git a/config-debug b/config-debug index ad06116c7..e7f7ce147 100644 --- a/config-debug +++ b/config-debug @@ -117,3 +117,7 @@ CONFIG_MAC80211_MESSAGE_TRACING=y CONFIG_EDAC_DEBUG=y CONFIG_LATENCYTOP=y CONFIG_SCHEDSTATS=y + +CONFIG_TEST_STRING_HELPERS=m +CONFIG_XFS_WARN=y + diff --git a/config-generic b/config-generic index 77d8e89ce..e80eb3172 100644 --- a/config-generic +++ b/config-generic @@ -188,6 +188,7 @@ CONFIG_FW_LOADER=y # CONFIG_FIRMWARE_IN_KERNEL is not set CONFIG_EXTRA_FIRMWARE="" +# Give this a try in rawhide for now # CONFIG_FW_LOADER_USER_HELPER is not set # CONFIG_CMA is not set @@ -2309,7 +2310,6 @@ CONFIG_SENSORS_MAX1619=m CONFIG_SENSORS_MAX6650=m CONFIG_SENSORS_MAX6697=m CONFIG_SENSORS_MCP3021=m -CONFIG_SENSORS_NCT6775=m CONFIG_SENSORS_NTC_THERMISTOR=m CONFIG_SENSORS_PC87360=m CONFIG_SENSORS_PC87427=m @@ -3999,7 +3999,8 @@ CONFIG_SECURITY_SELINUX_AVC_STATS=y # CONFIG_SECURITY_YAMA is not set CONFIG_AUDIT=y CONFIG_AUDITSYSCALL=y -# CONFIG_AUDIT_LOGINUID_IMMUTABLE is not set +# http://lists.fedoraproject.org/pipermail/kernel/2013-February/004125.html +CONFIG_AUDIT_LOGINUID_IMMUTABLE=y # # Cryptographic options @@ -4712,3 +4713,60 @@ CONFIG_IOMMU_SUPPORT=y # CONFIG_CRYPTO_KEY_TYPE is not set # CONFIG_PGP_LIBRARY is not set # CONFIG_PGP_PRELOAD is not set + + +# F18 3.10 rebase options below + +# CONFIG_ATH6KL_TRACING is not set +CONFIG_BATMAN_ADV_NC=y +# CONFIG_BCACHE_CLOSURES_DEBUG is not set +# CONFIG_BCACHE_DEBUG is not set +# CONFIG_BCACHE_EDEBUG is not set +CONFIG_BCACHE=m +CONFIG_BINFMT_SCRIPT=y +CONFIG_BOUNCE=y +# CONFIG_BTRFS_DEBUG is not set +# CONFIG_BTRFS_FS_RUN_SANITY_TESTS is not set +CONFIG_CRYPTO_CMAC=m +# CONFIG_DUMMY_IRQ is not set +# CONFIG_FB_SIMPLE is not set +# CONFIG_GPIO_GRGPIO is not set +CONFIG_HID_APPLEIR=m +# CONFIG_INPUT_IMS_PCU is not set +CONFIG_LEDS_LP5562=m +CONFIG_LEDS_TRIGGER_CAMERA=m +# CONFIG_MFD_CROS_EC is not set +# CONFIG_MFD_SI476X_CORE is not set +CONFIG_NETLINK_DIAG=m +CONFIG_NETLINK_MMAP=y +CONFIG_NET_TEAM_MODE_RANDOM=m +# CONFIG_RCU_USER_QS is not set +# CONFIG_RESET_CONTROLLER is not set +# CONFIG_RING_BUFFER_STARTUP_TEST is not set +CONFIG_RT2800USB_RT55XX=y +# CONFIG_SCSI_UFSHCD_PLATFORM is not set +CONFIG_SENSORS_ADT7310=m +CONFIG_SENSORS_LM95234=m +CONFIG_SENSORS_NCT6775=m +# CONFIG_SERIO_APBPS2 is not set +# CONFIG_SMS_SIANO_DEBUGFS is not set +# CONFIG_SRAM is not set +# CONFIG_SSBI is not set +# CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP is not set +CONFIG_USB_DEFAULT_PERSIST=y +CONFIG_USB_PHY=y +CONFIG_USB_RTL8152=m +# CONFIG_USB_SERIAL_WISHBONE is not set +# CONFIG_W1_SLAVE_DS2408_READBACK is not set +# CONFIG_SAMSUNG_USB2PHY is not set +# CONFIG_SAMSUNG_USB3PHY is not set +CONFIG_ALX=m +CONFIG_INFINIBAND_ISERT=m +CONFIG_QLCNIC_SRIOV=y +CONFIG_RTL8188EE=m +# CONFIG_SAMSUNG_USBPHY is not set +# CONFIG_TIPC_MEDIA_IB is not set +# CONFIG_USB_DWC2 is not set +CONFIG_VHOST_SCSI=m +# CONFIG_MFD_TPS65912 is not set +# CONFIG_MFD_SYSCON is not set diff --git a/config-nodebug b/config-nodebug index 6db2dde22..12ecfcc29 100644 --- a/config-nodebug +++ b/config-nodebug @@ -119,3 +119,7 @@ CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y # CONFIG_SPI_DEBUG is not set # CONFIG_LATENCYTOP is not set # CONFIG_SCHEDSTATS is not set + +# CONFIG_TEST_STRING_HELPERS is not set +# CONFIG_XFS_WARN is not set + diff --git a/config-powerpc-generic b/config-powerpc-generic index b6df88ea8..e8617230e 100644 --- a/config-powerpc-generic +++ b/config-powerpc-generic @@ -392,3 +392,5 @@ CONFIG_BACKLIGHT_PWM=m CONFIG_CPU_IDLE_MULTIPLE_DRIVERS=n CONFIG_XZ_DEC_POWERPC=y + +# CONFIG_POWERNV_MSI is not set diff --git a/config-powerpc64 b/config-powerpc64 index d0e0aab5b..02a44d888 100644 --- a/config-powerpc64 +++ b/config-powerpc64 @@ -181,3 +181,6 @@ CONFIG_BPF_JIT=y # CONFIG_PPC_TRANSACTIONAL_MEM is not set # CONFIG_SND_HDA_INTEL is not set CONFIG_BLK_DEV_RSXX=m + +CONFIG_POWERNV_MSI=y +CONFIG_KVM_XICS=y diff --git a/config-powerpc64p7 b/config-powerpc64p7 index 285d9fff9..ff4471037 100644 --- a/config-powerpc64p7 +++ b/config-powerpc64p7 @@ -171,3 +171,6 @@ CONFIG_BPF_JIT=y # CONFIG_PCIEPORTBUS is not set # CONFIG_SND_HDA_INTEL is not set CONFIG_BLK_DEV_RSXX=m + +CONFIG_POWERNV_MSI=y +CONFIG_KVM_XICS=y diff --git a/config-x86-generic b/config-x86-generic index 89bb5b0ef..911455e40 100644 --- a/config-x86-generic +++ b/config-x86-generic @@ -448,14 +448,27 @@ CONFIG_XZ_DEC_X86=y CONFIG_MPILIB=y CONFIG_PKCS7_MESSAGE_PARSER=y CONFIG_EFI_SIGNATURE_LIST_PARSER=y +CONFIG_PE_FILE_PARSER=y CONFIG_MODULE_SIG=y CONFIG_MODULE_SIG_ALL=y # CONFIG_MODULE_SIG_SHA1 is not set CONFIG_MODULE_SIG_SHA256=y # CONFIG_MODULE_SIG_FORCE is not set CONFIG_MODULE_SIG_BLACKLIST=y +CONFIG_SYSTEM_BLACKLIST_KEYRING=y CONFIG_MODULE_SIG_UEFI=y CONFIG_VMXNET3=m CONFIG_VFIO_PCI_VGA=y + +CONFIG_EFIVAR_FS=y +CONFIG_HYPERVISOR_GUEST=y +CONFIG_KVM_DEVICE_ASSIGNMENT=y +CONFIG_NFC_MEI_PHY=m +CONFIG_PVPANIC=m +CONFIG_X86_AMD_FREQ_SENSITIVITY=m +CONFIG_FB_HYPERV=m +CONFIG_NFC_MICROREAD_MEI=m +CONFIG_NFC_PN544_MEI=m + diff --git a/config-x86_64-generic b/config-x86_64-generic index 67ac161f3..0c460722f 100644 --- a/config-x86_64-generic +++ b/config-x86_64-generic @@ -97,6 +97,7 @@ CONFIG_XEN_MAX_DOMAIN_MEMORY=128 CONFIG_XEN_DEV_EVTCHN=m CONFIG_XEN_SYS_HYPERVISOR=y # CONFIG_XEN_MCE_LOG is not set +# CONFIG_XEN_STUB is not set CONFIG_PROVIDE_OHCI1394_DMA_INIT=y @@ -133,6 +134,11 @@ CONFIG_BPF_JIT=y CONFIG_NTB=m CONFIG_NTB_NETDEV=m +CONFIG_SFC=m +CONFIG_SFC_MCDI_MON=y +CONFIG_SFC_SRIOV=y +CONFIG_SFC_PTP=y + # 10GigE # CONFIG_IP1000=m @@ -148,3 +154,8 @@ CONFIG_SFC_MTD=y CONFIG_MTD_CHAR=m CONFIG_MTD_BLOCK=m +CONFIG_CRYPTO_CAMELLIA_AESNI_AVX2_X86_64=m +CONFIG_CRYPTO_SERPENT_AVX2_X86_64=m +CONFIG_CRYPTO_SHA256_SSSE3=m +CONFIG_CRYPTO_SHA512_SSSE3=m + diff --git a/cve-2013-4125.patch b/cve-2013-4125.patch deleted file mode 100644 index 25b7eca42..000000000 --- a/cve-2013-4125.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 307f2fb95e9b96b3577916e73d92e104f8f26494 Mon Sep 17 00:00:00 2001 -From: Hannes Frederic Sowa -Date: Fri, 12 Jul 2013 21:46:33 +0000 -Subject: ipv6: only static routes qualify for equal cost multipathing - -Static routes in this case are non-expiring routes which did not get -configured by autoconf or by icmpv6 redirects. - -To make sure we actually get an ecmp route while searching for the first -one in this fib6_node's leafs, also make sure it matches the ecmp route -assumptions. - -v2: -a) Removed RTF_EXPIRE check in dst.from chain. The check of RTF_ADDRCONF - already ensures that this route, even if added again without - RTF_EXPIRES (in case of a RA announcement with infinite timeout), - does not cause the rt6i_nsiblings logic to go wrong if a later RA - updates the expiration time later. - -v3: -a) Allow RTF_EXPIRES routes to enter the ecmp route set. We have to do so, - because an pmtu event could update the RTF_EXPIRES flag and we would - not count this route, if another route joins this set. We now filter - only for RTF_GATEWAY|RTF_ADDRCONF|RTF_DYNAMIC, which are flags that - don't get changed after rt6_info construction. - -Cc: Nicolas Dichtel -Signed-off-by: Hannes Frederic Sowa -Signed-off-by: David S. Miller ---- -diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c -index 192dd1a..5fc9c7a 100644 ---- a/net/ipv6/ip6_fib.c -+++ b/net/ipv6/ip6_fib.c -@@ -632,6 +632,12 @@ insert_above: - return ln; - } - -+static inline bool rt6_qualify_for_ecmp(struct rt6_info *rt) -+{ -+ return (rt->rt6i_flags & (RTF_GATEWAY|RTF_ADDRCONF|RTF_DYNAMIC)) == -+ RTF_GATEWAY; -+} -+ - /* - * Insert routing information in a node. - */ -@@ -646,6 +652,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, - int add = (!info->nlh || - (info->nlh->nlmsg_flags & NLM_F_CREATE)); - int found = 0; -+ bool rt_can_ecmp = rt6_qualify_for_ecmp(rt); - - ins = &fn->leaf; - -@@ -691,9 +698,8 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, - * To avoid long list, we only had siblings if the - * route have a gateway. - */ -- if (rt->rt6i_flags & RTF_GATEWAY && -- !(rt->rt6i_flags & RTF_EXPIRES) && -- !(iter->rt6i_flags & RTF_EXPIRES)) -+ if (rt_can_ecmp && -+ rt6_qualify_for_ecmp(iter)) - rt->rt6i_nsiblings++; - } - -@@ -715,7 +721,8 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, - /* Find the first route that have the same metric */ - sibling = fn->leaf; - while (sibling) { -- if (sibling->rt6i_metric == rt->rt6i_metric) { -+ if (sibling->rt6i_metric == rt->rt6i_metric && -+ rt6_qualify_for_ecmp(sibling)) { - list_add_tail(&rt->rt6i_siblings, - &sibling->rt6i_siblings); - break; --- -cgit v0.9.2 diff --git a/debug-bad-pte-dmi.patch b/debug-bad-pte-dmi.patch deleted file mode 100644 index eddd595b0..000000000 --- a/debug-bad-pte-dmi.patch +++ /dev/null @@ -1,66 +0,0 @@ ---- linux.orig/include/asm-generic/bug.h -+++ linux/include/asm-generic/bug.h -@@ -55,6 +55,8 @@ struct bug_entry { - #define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while(0) - #endif - -+void print_hardware_dmi_name(void); -+ - /* - * WARN(), WARN_ON(), WARN_ON_ONCE, and so on can be used to report - * significant issues that need prompt attention if they should ever ---- linux.orig/kernel/panic.c -+++ linux/kernel/panic.c -@@ -391,6 +391,15 @@ void oops_exit(void) - kmsg_dump(KMSG_DUMP_OOPS); - } - -+void print_hardware_dmi_name(void) -+{ -+ const char *board; -+ -+ board = dmi_get_system_info(DMI_PRODUCT_NAME); -+ if (board) -+ printk(KERN_WARNING "Hardware name: %s\n", board); -+} -+ - #ifdef WANT_WARN_ON_SLOWPATH - struct slowpath_args { - const char *fmt; -@@ -400,13 +409,10 @@ struct slowpath_args { - static void warn_slowpath_common(const char *file, int line, void *caller, - unsigned taint, struct slowpath_args *args) - { -- const char *board; -- - printk(KERN_WARNING "------------[ cut here ]------------\n"); - printk(KERN_WARNING "WARNING: at %s:%d %pS()\n", file, line, caller); -- board = dmi_get_system_info(DMI_PRODUCT_NAME); -- if (board) -- printk(KERN_WARNING "Hardware name: %s\n", board); -+ -+ print_hardware_dmi_name(); - - if (args) - vprintk(args->fmt, args->args); ---- linux.orig/mm/memory.c -+++ linux/mm/memory.c -@@ -706,6 +706,8 @@ static void print_bad_pte(struct vm_area - "BUG: Bad page map in process %s pte:%08llx pmd:%08llx\n", - current->comm, - (long long)pte_val(pte), (long long)pmd_val(*pmd)); -+ print_hardware_dmi_name(); -+ - if (page) - dump_page(page); - printk(KERN_ALERT ---- linux.orig/mm/page_alloc.c -+++ linux/mm/page_alloc.c -@@ -321,6 +321,7 @@ static void bad_page(struct page *page) - current->comm, page_to_pfn(page)); - dump_page(page); - -+ print_hardware_dmi_name(); - print_modules(); - dump_stack(); - out: diff --git a/debug-bad-pte-modules.patch b/debug-bad-pte-modules.patch deleted file mode 100644 index 0cc7d550e..000000000 --- a/debug-bad-pte-modules.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff -durpN '--exclude-from=/home/davej/.exclude' /home/davej/src/kernel/git-trees/linux/mm/memory.c linux-dj/mm/memory.c ---- /home/davej/src/kernel/git-trees/linux/mm/memory.c 2013-02-26 14:41:18.591116577 -0500 -+++ linux-dj/mm/memory.c 2013-02-28 20:04:37.678304092 -0500 -@@ -57,6 +57,7 @@ - #include - #include - #include -+#include - #include - #include - ---- linux-3.9.0-200.fc18.x86_64/mm/memory.c~ 2013-05-06 15:04:30.324416922 -0400 -+++ linux-3.9.0-200.fc18.x86_64/mm/memory.c 2013-05-06 15:04:43.933398227 -0400 -@@ -723,6 +723,7 @@ static void print_bad_pte(struct vm_area - if (vma->vm_file && vma->vm_file->f_op) - print_symbol(KERN_ALERT "vma->vm_file->f_op->mmap: %s\n", - (unsigned long)vma->vm_file->f_op->mmap); -+ print_modules(); - dump_stack(); - add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE); - } diff --git a/devel-pekey-secure-boot-20130502.patch b/devel-pekey-secure-boot-20130502.patch new file mode 100644 index 000000000..703bbf5ad --- /dev/null +++ b/devel-pekey-secure-boot-20130502.patch @@ -0,0 +1,5912 @@ +From 888c361d20210d39863ba6f2b71adb84e0a926a7 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Fri, 18 Jan 2013 13:53:35 +0000 +Subject: [PATCH 01/47] KEYS: Load *.x509 files into kernel keyring + +Load all the files matching the pattern "*.x509" that are to be found in kernel +base source dir and base build dir into the module signing keyring. + +The "extra_certificates" file is then redundant. + +Signed-off-by: David Howells +--- + kernel/Makefile | 35 +++++++++++++++++++++++++++++------ + kernel/modsign_certificate.S | 3 +-- + 2 files changed, 30 insertions(+), 8 deletions(-) + +diff --git a/kernel/Makefile b/kernel/Makefile +index d1574d4..64c97da 100644 +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -141,17 +141,40 @@ $(obj)/timeconst.h: $(obj)/hz.bc $(src)/timeconst.bc FORCE + $(call if_changed,bc) + + ifeq ($(CONFIG_MODULE_SIG),y) ++############################################################################### + # +-# Pull the signing certificate and any extra certificates into the kernel ++# Roll all the X.509 certificates that we can find together and pull ++# them into the kernel. + # ++############################################################################### ++X509_CERTIFICATES-y := $(wildcard *.x509) $(wildcard $(srctree)/*.x509) ++X509_CERTIFICATES-$(CONFIG_MODULE_SIG) += signing_key.x509 ++X509_CERTIFICATES := $(sort $(X509_CERTIFICATES-y)) ++ ++ifeq ($(X509_CERTIFICATES),) ++$(warning *** No X.509 certificates found ***) ++endif ++ ++ifneq ($(wildcard $(obj)/.x509.list),) ++ifneq ($(shell cat $(obj)/.x509.list),$(X509_CERTIFICATES)) ++$(info X.509 certificate list changed) ++$(shell rm $(obj)/.x509.list) ++endif ++endif ++ ++kernel/modsign_certificate.o: $(obj)/x509_certificate_list + +-quiet_cmd_touch = TOUCH $@ +- cmd_touch = touch $@ ++quiet_cmd_x509certs = CERTS $@ ++ cmd_x509certs = cat $(X509_CERTIFICATES) /dev/null >$@ ++targets += $(obj)/x509_certificate_list ++$(obj)/x509_certificate_list: $(X509_CERTIFICATES) $(obj)/.x509.list ++ $(call if_changed,x509certs) + +-extra_certificates: +- $(call cmd,touch) ++targets += $(obj)/.x509.list ++$(obj)/.x509.list: ++ @echo $(X509_CERTIFICATES) >$@ + +-kernel/modsign_certificate.o: signing_key.x509 extra_certificates ++clean-files := x509_certificate_list .x509.list + + ############################################################################### + # +diff --git a/kernel/modsign_certificate.S b/kernel/modsign_certificate.S +index 246b4c6..0a60203 100644 +--- a/kernel/modsign_certificate.S ++++ b/kernel/modsign_certificate.S +@@ -14,6 +14,5 @@ + .section ".init.data","aw" + + GLOBAL(modsign_certificate_list) +- .incbin "signing_key.x509" +- .incbin "extra_certificates" ++ .incbin "kernel/x509_certificate_list" + GLOBAL(modsign_certificate_list_end) +-- +1.8.1.4 + + +From 26a6bf8ffbe82d706c6de06746d760d9bc425ee5 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 18:39:54 +0000 +Subject: [PATCH 02/47] KEYS: Separate the kernel signature checking keyring + from module signing + +Separate the kernel signature checking keyring from module signing so that it +can be used by code other than the module-signing code. + +Signed-off-by: David Howells +--- + include/keys/system_keyring.h | 23 ++++++++++ + init/Kconfig | 13 ++++++ + kernel/Makefile | 17 ++++--- + kernel/modsign_pubkey.c | 104 ------------------------------------------ + kernel/module-internal.h | 2 - + kernel/module_signing.c | 3 +- + kernel/system_certificates.S | 18 ++++++++ + kernel/system_keyring.c | 101 ++++++++++++++++++++++++++++++++++++++++ + 8 files changed, 168 insertions(+), 113 deletions(-) + create mode 100644 include/keys/system_keyring.h + delete mode 100644 kernel/modsign_pubkey.c + create mode 100644 kernel/system_certificates.S + create mode 100644 kernel/system_keyring.c + +diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h +new file mode 100644 +index 0000000..8dabc39 +--- /dev/null ++++ b/include/keys/system_keyring.h +@@ -0,0 +1,23 @@ ++/* System keyring containing trusted public keys. ++ * ++ * Copyright (C) 2013 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. ++ */ ++ ++#ifndef _KEYS_SYSTEM_KEYRING_H ++#define _KEYS_SYSTEM_KEYRING_H ++ ++#ifdef CONFIG_SYSTEM_TRUSTED_KEYRING ++ ++#include ++ ++extern struct key *system_trusted_keyring; ++ ++#endif ++ ++#endif /* _KEYS_SYSTEM_KEYRING_H */ +diff --git a/init/Kconfig b/init/Kconfig +index a76d131..b9d8870 100644 +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1615,6 +1615,18 @@ config BASE_SMALL + default 0 if BASE_FULL + default 1 if !BASE_FULL + ++config SYSTEM_TRUSTED_KEYRING ++ bool "Provide system-wide ring of trusted keys" ++ depends on KEYS ++ help ++ Provide a system keyring to which trusted keys can be added. Keys in ++ the keyring are considered to be trusted. Keys may be added at will ++ by the kernel from compiled-in data and from hardware key stores, but ++ userspace may only add extra keys if those keys can be verified by ++ keys already in the keyring. ++ ++ Keys in this keyring are used by module signature checking. ++ + menuconfig MODULES + bool "Enable loadable module support" + help +@@ -1687,6 +1699,7 @@ config MODULE_SRCVERSION_ALL + config MODULE_SIG + bool "Module signature verification" + depends on MODULES ++ select SYSTEM_TRUSTED_KEYRING + select KEYS + select CRYPTO + select ASYMMETRIC_KEY_TYPE +diff --git a/kernel/Makefile b/kernel/Makefile +index 64c97da..ecff938 100644 +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -52,8 +52,9 @@ obj-$(CONFIG_SMP) += spinlock.o + obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o + obj-$(CONFIG_PROVE_LOCKING) += spinlock.o + obj-$(CONFIG_UID16) += uid16.o ++obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o + obj-$(CONFIG_MODULES) += module.o +-obj-$(CONFIG_MODULE_SIG) += module_signing.o modsign_pubkey.o modsign_certificate.o ++obj-$(CONFIG_MODULE_SIG) += module_signing.o + obj-$(CONFIG_KALLSYMS) += kallsyms.o + obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o + obj-$(CONFIG_KEXEC) += kexec.o +@@ -140,13 +141,14 @@ targets += timeconst.h + $(obj)/timeconst.h: $(obj)/hz.bc $(src)/timeconst.bc FORCE + $(call if_changed,bc) + +-ifeq ($(CONFIG_MODULE_SIG),y) + ############################################################################### + # +-# Roll all the X.509 certificates that we can find together and pull +-# them into the kernel. ++# Roll all the X.509 certificates that we can find together and pull them into ++# the kernel so that they get loaded into the system trusted keyring during ++# boot. + # + ############################################################################### ++ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y) + X509_CERTIFICATES-y := $(wildcard *.x509) $(wildcard $(srctree)/*.x509) + X509_CERTIFICATES-$(CONFIG_MODULE_SIG) += signing_key.x509 + X509_CERTIFICATES := $(sort $(X509_CERTIFICATES-y)) +@@ -162,10 +164,11 @@ $(shell rm $(obj)/.x509.list) + endif + endif + +-kernel/modsign_certificate.o: $(obj)/x509_certificate_list ++kernel/system_certificates.o: $(obj)/x509_certificate_list + + quiet_cmd_x509certs = CERTS $@ +- cmd_x509certs = cat $(X509_CERTIFICATES) /dev/null >$@ ++ cmd_x509certs = cat $(X509_CERTIFICATES) /dev/null >$@ $(foreach X509,$(X509_CERTIFICATES),; echo " - Including cert $(X509)") ++ + targets += $(obj)/x509_certificate_list + $(obj)/x509_certificate_list: $(X509_CERTIFICATES) $(obj)/.x509.list + $(call if_changed,x509certs) +@@ -175,7 +178,9 @@ $(obj)/.x509.list: + @echo $(X509_CERTIFICATES) >$@ + + clean-files := x509_certificate_list .x509.list ++endif + ++ifeq ($(CONFIG_MODULE_SIG),y) + ############################################################################### + # + # If module signing is requested, say by allyesconfig, but a key has not been +diff --git a/kernel/modsign_pubkey.c b/kernel/modsign_pubkey.c +deleted file mode 100644 +index 2b6e699..0000000 +--- a/kernel/modsign_pubkey.c ++++ /dev/null +@@ -1,104 +0,0 @@ +-/* Public keys for module signature verification +- * +- * Copyright (C) 2012 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. +- */ +- +-#include +-#include +-#include +-#include +-#include +-#include "module-internal.h" +- +-struct key *modsign_keyring; +- +-extern __initdata const u8 modsign_certificate_list[]; +-extern __initdata const u8 modsign_certificate_list_end[]; +- +-/* +- * We need to make sure ccache doesn't cache the .o file as it doesn't notice +- * if modsign.pub changes. +- */ +-static __initdata const char annoy_ccache[] = __TIME__ "foo"; +- +-/* +- * Load the compiled-in keys +- */ +-static __init int module_verify_init(void) +-{ +- pr_notice("Initialise module verification\n"); +- +- modsign_keyring = keyring_alloc(".module_sign", +- KUIDT_INIT(0), KGIDT_INIT(0), +- current_cred(), +- ((KEY_POS_ALL & ~KEY_POS_SETATTR) | +- KEY_USR_VIEW | KEY_USR_READ), +- KEY_ALLOC_NOT_IN_QUOTA, NULL); +- if (IS_ERR(modsign_keyring)) +- panic("Can't allocate module signing keyring\n"); +- +- return 0; +-} +- +-/* +- * Must be initialised before we try and load the keys into the keyring. +- */ +-device_initcall(module_verify_init); +- +-/* +- * Load the compiled-in keys +- */ +-static __init int load_module_signing_keys(void) +-{ +- key_ref_t key; +- const u8 *p, *end; +- size_t plen; +- +- pr_notice("Loading module verification certificates\n"); +- +- end = modsign_certificate_list_end; +- p = modsign_certificate_list; +- while (p < end) { +- /* Each cert begins with an ASN.1 SEQUENCE tag and must be more +- * than 256 bytes in size. +- */ +- if (end - p < 4) +- goto dodgy_cert; +- if (p[0] != 0x30 && +- p[1] != 0x82) +- goto dodgy_cert; +- plen = (p[2] << 8) | p[3]; +- plen += 4; +- if (plen > end - p) +- goto dodgy_cert; +- +- key = key_create_or_update(make_key_ref(modsign_keyring, 1), +- "asymmetric", +- NULL, +- p, +- plen, +- (KEY_POS_ALL & ~KEY_POS_SETATTR) | +- KEY_USR_VIEW, +- KEY_ALLOC_NOT_IN_QUOTA); +- if (IS_ERR(key)) +- pr_err("MODSIGN: Problem loading in-kernel X.509 certificate (%ld)\n", +- PTR_ERR(key)); +- else +- pr_notice("MODSIGN: Loaded cert '%s'\n", +- key_ref_to_ptr(key)->description); +- p += plen; +- } +- +- return 0; +- +-dodgy_cert: +- pr_err("MODSIGN: Problem parsing in-kernel X.509 certificate list\n"); +- return 0; +-} +-late_initcall(load_module_signing_keys); +diff --git a/kernel/module-internal.h b/kernel/module-internal.h +index 24f9247..915e123 100644 +--- a/kernel/module-internal.h ++++ b/kernel/module-internal.h +@@ -9,6 +9,4 @@ + * 2 of the Licence, or (at your option) any later version. + */ + +-extern struct key *modsign_keyring; +- + extern int mod_verify_sig(const void *mod, unsigned long *_modlen); +diff --git a/kernel/module_signing.c b/kernel/module_signing.c +index f2970bd..0034e36 100644 +--- a/kernel/module_signing.c ++++ b/kernel/module_signing.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include "module-internal.h" + + /* +@@ -157,7 +158,7 @@ static struct key *request_asymmetric_key(const char *signer, size_t signer_len, + + pr_debug("Look up: \"%s\"\n", id); + +- key = keyring_search(make_key_ref(modsign_keyring, 1), ++ key = keyring_search(make_key_ref(system_trusted_keyring, 1), + &key_type_asymmetric, id); + if (IS_ERR(key)) + pr_warn("Request for unknown module key '%s' err %ld\n", +diff --git a/kernel/system_certificates.S b/kernel/system_certificates.S +new file mode 100644 +index 0000000..86240df +--- /dev/null ++++ b/kernel/system_certificates.S +@@ -0,0 +1,18 @@ ++/* SYMBOL_PREFIX defined on commandline from CONFIG_SYMBOL_PREFIX */ ++#ifndef SYMBOL_PREFIX ++#define ASM_SYMBOL(sym) sym ++#else ++#define PASTE2(x,y) x##y ++#define PASTE(x,y) PASTE2(x,y) ++#define ASM_SYMBOL(sym) PASTE(SYMBOL_PREFIX, sym) ++#endif ++ ++#define GLOBAL(name) \ ++ .globl ASM_SYMBOL(name); \ ++ ASM_SYMBOL(name): ++ ++ .section ".init.data","aw" ++ ++GLOBAL(system_certificate_list) ++ .incbin "kernel/x509_certificate_list" ++GLOBAL(system_certificate_list_end) +diff --git a/kernel/system_keyring.c b/kernel/system_keyring.c +new file mode 100644 +index 0000000..a3ca76f +--- /dev/null ++++ b/kernel/system_keyring.c +@@ -0,0 +1,101 @@ ++/* System trusted keyring for trusted public keys ++ * ++ * Copyright (C) 2012 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. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "module-internal.h" ++ ++struct key *system_trusted_keyring; ++EXPORT_SYMBOL_GPL(system_trusted_keyring); ++ ++extern __initdata const u8 system_certificate_list[]; ++extern __initdata const u8 system_certificate_list_end[]; ++ ++/* ++ * Load the compiled-in keys ++ */ ++static __init int system_trusted_keyring_init(void) ++{ ++ pr_notice("Initialise system trusted keyring\n"); ++ ++ system_trusted_keyring = ++ keyring_alloc(".system_keyring", ++ KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), ++ ((KEY_POS_ALL & ~KEY_POS_SETATTR) | ++ KEY_USR_VIEW | KEY_USR_READ), ++ KEY_ALLOC_NOT_IN_QUOTA, NULL); ++ if (IS_ERR(system_trusted_keyring)) ++ panic("Can't allocate system trusted keyring\n"); ++ ++ return 0; ++} ++ ++/* ++ * Must be initialised before we try and load the keys into the keyring. ++ */ ++device_initcall(system_trusted_keyring_init); ++ ++/* ++ * Load the compiled-in list of X.509 certificates. ++ */ ++static __init int load_system_certificate_list(void) ++{ ++ key_ref_t key; ++ const u8 *p, *end; ++ size_t plen; ++ ++ pr_notice("Loading compiled-in X.509 certificates\n"); ++ ++ end = system_certificate_list_end; ++ p = system_certificate_list; ++ while (p < end) { ++ /* Each cert begins with an ASN.1 SEQUENCE tag and must be more ++ * than 256 bytes in size. ++ */ ++ if (end - p < 4) ++ goto dodgy_cert; ++ if (p[0] != 0x30 && ++ p[1] != 0x82) ++ goto dodgy_cert; ++ plen = (p[2] << 8) | p[3]; ++ plen += 4; ++ if (plen > end - p) ++ goto dodgy_cert; ++ ++ key = key_create_or_update(make_key_ref(system_trusted_keyring, 1), ++ "asymmetric", ++ NULL, ++ p, ++ plen, ++ (KEY_POS_ALL & ~KEY_POS_SETATTR) | ++ KEY_USR_VIEW, ++ KEY_ALLOC_NOT_IN_QUOTA); ++ if (IS_ERR(key)) ++ pr_err("Problem loading in-kernel X.509 certificate (%ld)\n", ++ PTR_ERR(key)); ++ else ++ pr_notice("Loaded X.509 cert '%s'\n", ++ key_ref_to_ptr(key)->description); ++ p += plen; ++ } ++ ++ return 0; ++ ++dodgy_cert: ++ pr_err("Problem parsing in-kernel X.509 certificate list\n"); ++ return 0; ++} ++late_initcall(load_system_certificate_list); +-- +1.8.1.4 + + +From 4e2b0f425d73360fc40b8719b36e6e3ca94d458e Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Thu, 17 Jan 2013 16:25:00 +0000 +Subject: [PATCH 03/47] KEYS: Add a 'trusted' flag and a 'trusted only' flag + +Add KEY_FLAG_TRUSTED to indicate that a key either comes from a trusted source +or had a cryptographic signature chain that led back to a trusted key the +kernel already possessed. + +Add KEY_FLAGS_TRUSTED_ONLY to indicate that a keyring will only accept links to +keys marked with KEY_FLAGS_TRUSTED. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +--- + include/linux/key-type.h | 1 + + include/linux/key.h | 3 +++ + kernel/system_keyring.c | 4 +++- + security/keys/key.c | 8 ++++++++ + security/keys/keyring.c | 4 ++++ + 5 files changed, 19 insertions(+), 1 deletion(-) + +diff --git a/include/linux/key-type.h b/include/linux/key-type.h +index 518a53a..f942b2d 100644 +--- a/include/linux/key-type.h ++++ b/include/linux/key-type.h +@@ -45,6 +45,7 @@ struct key_preparsed_payload { + const void *data; /* Raw data */ + size_t datalen; /* Raw datalen */ + size_t quotalen; /* Quota length for proposed payload */ ++ bool trusted; /* True if key is trusted */ + }; + + typedef int (*request_key_actor_t)(struct key_construction *key, +diff --git a/include/linux/key.h b/include/linux/key.h +index 4dfde11..0b32a09 100644 +--- a/include/linux/key.h ++++ b/include/linux/key.h +@@ -162,6 +162,8 @@ struct key { + #define KEY_FLAG_NEGATIVE 5 /* set if key is negative */ + #define KEY_FLAG_ROOT_CAN_CLEAR 6 /* set if key can be cleared by root without permission */ + #define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */ ++#define KEY_FLAG_TRUSTED 8 /* set if key is trusted */ ++#define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */ + + /* the description string + * - this is used to match a key against search criteria +@@ -203,6 +205,7 @@ extern struct key *key_alloc(struct key_type *type, + #define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ + #define KEY_ALLOC_QUOTA_OVERRUN 0x0001 /* add to quota, permit even if overrun */ + #define KEY_ALLOC_NOT_IN_QUOTA 0x0002 /* not in quota */ ++#define KEY_ALLOC_TRUSTED 0x0004 /* Key should be flagged as trusted */ + + extern void key_revoke(struct key *key); + extern void key_invalidate(struct key *key); +diff --git a/kernel/system_keyring.c b/kernel/system_keyring.c +index a3ca76f..dae8778 100644 +--- a/kernel/system_keyring.c ++++ b/kernel/system_keyring.c +@@ -40,6 +40,7 @@ static __init int system_trusted_keyring_init(void) + if (IS_ERR(system_trusted_keyring)) + panic("Can't allocate system trusted keyring\n"); + ++ set_bit(KEY_FLAG_TRUSTED_ONLY, &system_trusted_keyring->flags); + return 0; + } + +@@ -82,7 +83,8 @@ static __init int load_system_certificate_list(void) + plen, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW, +- KEY_ALLOC_NOT_IN_QUOTA); ++ KEY_ALLOC_NOT_IN_QUOTA | ++ KEY_ALLOC_TRUSTED); + if (IS_ERR(key)) + pr_err("Problem loading in-kernel X.509 certificate (%ld)\n", + PTR_ERR(key)); +diff --git a/security/keys/key.c b/security/keys/key.c +index 8fb7c7b..f3de9e4 100644 +--- a/security/keys/key.c ++++ b/security/keys/key.c +@@ -299,6 +299,8 @@ struct key *key_alloc(struct key_type *type, const char *desc, + + if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) + key->flags |= 1 << KEY_FLAG_IN_QUOTA; ++ if (flags & KEY_ALLOC_TRUSTED) ++ key->flags |= 1 << KEY_FLAG_TRUSTED; + + memset(&key->type_data, 0, sizeof(key->type_data)); + +@@ -813,6 +815,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, + prep.data = payload; + prep.datalen = plen; + prep.quotalen = ktype->def_datalen; ++ prep.trusted = flags & KEY_ALLOC_TRUSTED; + if (ktype->preparse) { + ret = ktype->preparse(&prep); + if (ret < 0) { +@@ -826,6 +829,11 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, + goto error_free_prep; + } + ++ key_ref = ERR_PTR(-EPERM); ++ if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags)) ++ goto error_free_prep; ++ flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0; ++ + ret = __key_link_begin(keyring, ktype, description, &prealloc); + if (ret < 0) { + key_ref = ERR_PTR(ret); +diff --git a/security/keys/keyring.c b/security/keys/keyring.c +index 6ece7f2..f18d7ff 100644 +--- a/security/keys/keyring.c ++++ b/security/keys/keyring.c +@@ -1006,6 +1006,10 @@ int key_link(struct key *keyring, struct key *key) + key_check(keyring); + key_check(key); + ++ if (test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags) && ++ !test_bit(KEY_FLAG_TRUSTED, &key->flags)) ++ return -EPERM; ++ + ret = __key_link_begin(keyring, key->type, key->description, &prealloc); + if (ret == 0) { + ret = __key_link_check_live_key(keyring, key); +-- +1.8.1.4 + + +From 3deae827abdd3de9b7976b423279812d7559e580 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:32 +0000 +Subject: [PATCH 04/47] KEYS: Rename public key parameter name arrays + +Rename the arrays of public key parameters (public key algorithm names, hash +algorithm names and ID type names) so that the array name ends in "_name". + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +Reviewed-by: Josh Boyer +--- + crypto/asymmetric_keys/public_key.c | 14 +++++++------- + crypto/asymmetric_keys/x509_public_key.c | 8 ++++---- + include/crypto/public_key.h | 6 +++--- + kernel/module_signing.c | 4 ++-- + 4 files changed, 16 insertions(+), 16 deletions(-) + +diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c +index cb2e291..b313df1 100644 +--- a/crypto/asymmetric_keys/public_key.c ++++ b/crypto/asymmetric_keys/public_key.c +@@ -22,13 +22,13 @@ + + MODULE_LICENSE("GPL"); + +-const char *const pkey_algo[PKEY_ALGO__LAST] = { ++const char *const pkey_algo_name[PKEY_ALGO__LAST] = { + [PKEY_ALGO_DSA] = "DSA", + [PKEY_ALGO_RSA] = "RSA", + }; +-EXPORT_SYMBOL_GPL(pkey_algo); ++EXPORT_SYMBOL_GPL(pkey_algo_name); + +-const char *const pkey_hash_algo[PKEY_HASH__LAST] = { ++const char *const pkey_hash_algo_name[PKEY_HASH__LAST] = { + [PKEY_HASH_MD4] = "md4", + [PKEY_HASH_MD5] = "md5", + [PKEY_HASH_SHA1] = "sha1", +@@ -38,13 +38,13 @@ const char *const pkey_hash_algo[PKEY_HASH__LAST] = { + [PKEY_HASH_SHA512] = "sha512", + [PKEY_HASH_SHA224] = "sha224", + }; +-EXPORT_SYMBOL_GPL(pkey_hash_algo); ++EXPORT_SYMBOL_GPL(pkey_hash_algo_name); + +-const char *const pkey_id_type[PKEY_ID_TYPE__LAST] = { ++const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST] = { + [PKEY_ID_PGP] = "PGP", + [PKEY_ID_X509] = "X509", + }; +-EXPORT_SYMBOL_GPL(pkey_id_type); ++EXPORT_SYMBOL_GPL(pkey_id_type_name); + + /* + * Provide a part of a description of the key for /proc/keys. +@@ -56,7 +56,7 @@ static void public_key_describe(const struct key *asymmetric_key, + + if (key) + seq_printf(m, "%s.%s", +- pkey_id_type[key->id_type], key->algo->name); ++ pkey_id_type_name[key->id_type], key->algo->name); + } + + /* +diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c +index 06007f0..afbbc36 100644 +--- a/crypto/asymmetric_keys/x509_public_key.c ++++ b/crypto/asymmetric_keys/x509_public_key.c +@@ -49,7 +49,7 @@ static int x509_check_signature(const struct public_key *pub, + /* Allocate the hashing algorithm we're going to need and find out how + * big the hash operational data will be. + */ +- tfm = crypto_alloc_shash(pkey_hash_algo[cert->sig_hash_algo], 0, 0); ++ tfm = crypto_alloc_shash(pkey_hash_algo_name[cert->sig_hash_algo], 0, 0); + if (IS_ERR(tfm)) + return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); + +@@ -117,7 +117,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) + + pr_devel("Cert Issuer: %s\n", cert->issuer); + pr_devel("Cert Subject: %s\n", cert->subject); +- pr_devel("Cert Key Algo: %s\n", pkey_algo[cert->pkey_algo]); ++ pr_devel("Cert Key Algo: %s\n", pkey_algo_name[cert->pkey_algo]); + pr_devel("Cert Valid From: %04ld-%02d-%02d %02d:%02d:%02d\n", + cert->valid_from.tm_year + 1900, cert->valid_from.tm_mon + 1, + cert->valid_from.tm_mday, cert->valid_from.tm_hour, +@@ -127,8 +127,8 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) + cert->valid_to.tm_mday, cert->valid_to.tm_hour, + cert->valid_to.tm_min, cert->valid_to.tm_sec); + pr_devel("Cert Signature: %s + %s\n", +- pkey_algo[cert->sig_pkey_algo], +- pkey_hash_algo[cert->sig_hash_algo]); ++ pkey_algo_name[cert->sig_pkey_algo], ++ pkey_hash_algo_name[cert->sig_hash_algo]); + + if (!cert->fingerprint || !cert->authority) { + pr_warn("Cert for '%s' must have SubjKeyId and AuthKeyId extensions\n", +diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h +index f5b0224..619d570 100644 +--- a/include/crypto/public_key.h ++++ b/include/crypto/public_key.h +@@ -22,7 +22,7 @@ enum pkey_algo { + PKEY_ALGO__LAST + }; + +-extern const char *const pkey_algo[PKEY_ALGO__LAST]; ++extern const char *const pkey_algo_name[PKEY_ALGO__LAST]; + + enum pkey_hash_algo { + PKEY_HASH_MD4, +@@ -36,7 +36,7 @@ enum pkey_hash_algo { + PKEY_HASH__LAST + }; + +-extern const char *const pkey_hash_algo[PKEY_HASH__LAST]; ++extern const char *const pkey_hash_algo_name[PKEY_HASH__LAST]; + + enum pkey_id_type { + PKEY_ID_PGP, /* OpenPGP generated key ID */ +@@ -44,7 +44,7 @@ enum pkey_id_type { + PKEY_ID_TYPE__LAST + }; + +-extern const char *const pkey_id_type[PKEY_ID_TYPE__LAST]; ++extern const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST]; + + /* + * Cryptographic data for the public-key subtype of the asymmetric key type. +diff --git a/kernel/module_signing.c b/kernel/module_signing.c +index 0034e36..0b6b870 100644 +--- a/kernel/module_signing.c ++++ b/kernel/module_signing.c +@@ -55,7 +55,7 @@ static struct public_key_signature *mod_make_digest(enum pkey_hash_algo hash, + /* Allocate the hashing algorithm we're going to need and find out how + * big the hash operational data will be. + */ +- tfm = crypto_alloc_shash(pkey_hash_algo[hash], 0, 0); ++ tfm = crypto_alloc_shash(pkey_hash_algo_name[hash], 0, 0); + if (IS_ERR(tfm)) + return (PTR_ERR(tfm) == -ENOENT) ? ERR_PTR(-ENOPKG) : ERR_CAST(tfm); + +@@ -218,7 +218,7 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen) + return -ENOPKG; + + if (ms.hash >= PKEY_HASH__LAST || +- !pkey_hash_algo[ms.hash]) ++ !pkey_hash_algo_name[ms.hash]) + return -ENOPKG; + + key = request_asymmetric_key(sig, ms.signer_len, +-- +1.8.1.4 + + +From 2acf1a703de1213ad85515a71873f57535dc057d Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:33 +0000 +Subject: [PATCH 05/47] KEYS: Move the algorithm pointer array from x509 to + public_key.c + +Move the public-key algorithm pointer array from x509_public_key.c to +public_key.c as it isn't X.509 specific. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +Reviewed-by: Josh Boyer +--- + crypto/asymmetric_keys/public_key.c | 8 ++++++++ + crypto/asymmetric_keys/x509_public_key.c | 11 +---------- + include/crypto/public_key.h | 1 + + 3 files changed, 10 insertions(+), 10 deletions(-) + +diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c +index b313df1..796ce08 100644 +--- a/crypto/asymmetric_keys/public_key.c ++++ b/crypto/asymmetric_keys/public_key.c +@@ -28,6 +28,14 @@ const char *const pkey_algo_name[PKEY_ALGO__LAST] = { + }; + EXPORT_SYMBOL_GPL(pkey_algo_name); + ++const struct public_key_algorithm *pkey_algo[PKEY_ALGO__LAST] = { ++#if defined(CONFIG_PUBLIC_KEY_ALGO_RSA) || \ ++ defined(CONFIG_PUBLIC_KEY_ALGO_RSA_MODULE) ++ [PKEY_ALGO_RSA] = &RSA_public_key_algorithm, ++#endif ++}; ++EXPORT_SYMBOL_GPL(pkey_algo); ++ + const char *const pkey_hash_algo_name[PKEY_HASH__LAST] = { + [PKEY_HASH_MD4] = "md4", + [PKEY_HASH_MD5] = "md5", +diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c +index afbbc36..fe38628 100644 +--- a/crypto/asymmetric_keys/x509_public_key.c ++++ b/crypto/asymmetric_keys/x509_public_key.c +@@ -23,15 +23,6 @@ + #include "public_key.h" + #include "x509_parser.h" + +-static const +-struct public_key_algorithm *x509_public_key_algorithms[PKEY_ALGO__LAST] = { +- [PKEY_ALGO_DSA] = NULL, +-#if defined(CONFIG_PUBLIC_KEY_ALGO_RSA) || \ +- defined(CONFIG_PUBLIC_KEY_ALGO_RSA_MODULE) +- [PKEY_ALGO_RSA] = &RSA_public_key_algorithm, +-#endif +-}; +- + /* + * Check the signature on a certificate using the provided public key + */ +@@ -174,7 +165,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) + goto error_free_cert; + } + +- cert->pub->algo = x509_public_key_algorithms[cert->pkey_algo]; ++ cert->pub->algo = pkey_algo[cert->pkey_algo]; + cert->pub->id_type = PKEY_ID_X509; + + /* Check the signature on the key */ +diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h +index 619d570..46bde25 100644 +--- a/include/crypto/public_key.h ++++ b/include/crypto/public_key.h +@@ -23,6 +23,7 @@ enum pkey_algo { + }; + + extern const char *const pkey_algo_name[PKEY_ALGO__LAST]; ++extern const struct public_key_algorithm *pkey_algo[PKEY_ALGO__LAST]; + + enum pkey_hash_algo { + PKEY_HASH_MD4, +-- +1.8.1.4 + + +From 3cc2c6f01277dfa00106c3e4f3f3ab8184025b90 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:33 +0000 +Subject: [PATCH 06/47] KEYS: Store public key algo ID in public_key struct + +Store public key algo ID in public_key struct for reference purposes. This +allows it to be removed from the x509_certificate struct and used to find a +default in public_key_verify_signature(). + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +Reviewed-by: Josh Boyer +--- + crypto/asymmetric_keys/x509_cert_parser.c | 5 +++-- + crypto/asymmetric_keys/x509_parser.h | 1 - + crypto/asymmetric_keys/x509_public_key.c | 4 ++-- + include/crypto/public_key.h | 1 + + 4 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c +index 7fabc4c..a583930 100644 +--- a/crypto/asymmetric_keys/x509_cert_parser.c ++++ b/crypto/asymmetric_keys/x509_cert_parser.c +@@ -343,8 +343,9 @@ int x509_extract_key_data(void *context, size_t hdrlen, + if (ctx->last_oid != OID_rsaEncryption) + return -ENOPKG; + +- /* There seems to be an extraneous 0 byte on the front of the data */ +- ctx->cert->pkey_algo = PKEY_ALGO_RSA; ++ ctx->cert->pub->pkey_algo = PKEY_ALGO_RSA; ++ ++ /* Discard the BIT STRING metadata */ + ctx->key = value + 1; + ctx->key_size = vlen - 1; + return 0; +diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h +index f86dc5f..e583ad0 100644 +--- a/crypto/asymmetric_keys/x509_parser.h ++++ b/crypto/asymmetric_keys/x509_parser.h +@@ -20,7 +20,6 @@ struct x509_certificate { + char *authority; /* Authority key fingerprint as hex */ + struct tm valid_from; + struct tm valid_to; +- enum pkey_algo pkey_algo : 8; /* Public key algorithm */ + enum pkey_algo sig_pkey_algo : 8; /* Signature public key algorithm */ + enum pkey_hash_algo sig_hash_algo : 8; /* Signature hash algorithm */ + const void *tbs; /* Signed data */ +diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c +index fe38628..fac574c 100644 +--- a/crypto/asymmetric_keys/x509_public_key.c ++++ b/crypto/asymmetric_keys/x509_public_key.c +@@ -108,7 +108,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) + + pr_devel("Cert Issuer: %s\n", cert->issuer); + pr_devel("Cert Subject: %s\n", cert->subject); +- pr_devel("Cert Key Algo: %s\n", pkey_algo_name[cert->pkey_algo]); ++ pr_devel("Cert Key Algo: %s\n", pkey_algo_name[cert->pub->pkey_algo]); + pr_devel("Cert Valid From: %04ld-%02d-%02d %02d:%02d:%02d\n", + cert->valid_from.tm_year + 1900, cert->valid_from.tm_mon + 1, + cert->valid_from.tm_mday, cert->valid_from.tm_hour, +@@ -165,7 +165,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) + goto error_free_cert; + } + +- cert->pub->algo = pkey_algo[cert->pkey_algo]; ++ cert->pub->algo = pkey_algo[cert->pub->pkey_algo]; + cert->pub->id_type = PKEY_ID_X509; + + /* Check the signature on the key */ +diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h +index 46bde25..05778df 100644 +--- a/include/crypto/public_key.h ++++ b/include/crypto/public_key.h +@@ -60,6 +60,7 @@ struct public_key { + #define PKEY_CAN_DECRYPT 0x02 + #define PKEY_CAN_SIGN 0x04 + #define PKEY_CAN_VERIFY 0x08 ++ enum pkey_algo pkey_algo : 8; + enum pkey_id_type id_type : 8; + union { + MPI mpi[5]; +-- +1.8.1.4 + + +From 7dcc63793a873198d3b3c4299f896e2896292d84 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:34 +0000 +Subject: [PATCH 07/47] KEYS: Split public_key_verify_signature() and make + available + +Modify public_key_verify_signature() so that it now takes a public_key struct +rather than a key struct and supply a wrapper that takes a key struct. The +wrapper is then used by the asymmetric key subtype and the modified function is +used by X.509 self-signature checking and can be used by PKCS#7 also. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +Reviewed-by: Josh Boyer +--- + crypto/asymmetric_keys/public_key.c | 40 +++++++++++++++++++++++++------- + crypto/asymmetric_keys/public_key.h | 6 +++++ + crypto/asymmetric_keys/x509_public_key.c | 2 +- + 3 files changed, 39 insertions(+), 9 deletions(-) + +diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c +index 796ce08..49ac8d8 100644 +--- a/crypto/asymmetric_keys/public_key.c ++++ b/crypto/asymmetric_keys/public_key.c +@@ -86,21 +86,45 @@ EXPORT_SYMBOL_GPL(public_key_destroy); + /* + * Verify a signature using a public key. + */ +-static int public_key_verify_signature(const struct key *key, +- const struct public_key_signature *sig) ++int public_key_verify_signature(const struct public_key *pk, ++ const struct public_key_signature *sig) + { +- const struct public_key *pk = key->payload.data; ++ const struct public_key_algorithm *algo; ++ ++ BUG_ON(!pk); ++ BUG_ON(!pk->mpi[0]); ++ BUG_ON(!pk->mpi[1]); ++ BUG_ON(!sig); ++ BUG_ON(!sig->digest); ++ BUG_ON(!sig->mpi[0]); ++ ++ algo = pk->algo; ++ if (!algo) { ++ if (pk->pkey_algo >= PKEY_ALGO__LAST) ++ return -ENOPKG; ++ algo = pkey_algo[pk->pkey_algo]; ++ if (!algo) ++ return -ENOPKG; ++ } + +- if (!pk->algo->verify_signature) ++ if (!algo->verify_signature) + return -ENOTSUPP; + +- if (sig->nr_mpi != pk->algo->n_sig_mpi) { ++ if (sig->nr_mpi != algo->n_sig_mpi) { + pr_debug("Signature has %u MPI not %u\n", +- sig->nr_mpi, pk->algo->n_sig_mpi); ++ sig->nr_mpi, algo->n_sig_mpi); + return -EINVAL; + } + +- return pk->algo->verify_signature(pk, sig); ++ return algo->verify_signature(pk, sig); ++} ++EXPORT_SYMBOL_GPL(public_key_verify_signature); ++ ++static int public_key_verify_signature_2(const struct key *key, ++ const struct public_key_signature *sig) ++{ ++ const struct public_key *pk = key->payload.data; ++ return public_key_verify_signature(pk, sig); + } + + /* +@@ -111,6 +135,6 @@ struct asymmetric_key_subtype public_key_subtype = { + .name = "public_key", + .describe = public_key_describe, + .destroy = public_key_destroy, +- .verify_signature = public_key_verify_signature, ++ .verify_signature = public_key_verify_signature_2, + }; + EXPORT_SYMBOL_GPL(public_key_subtype); +diff --git a/crypto/asymmetric_keys/public_key.h b/crypto/asymmetric_keys/public_key.h +index 5e5e356..5c37a22 100644 +--- a/crypto/asymmetric_keys/public_key.h ++++ b/crypto/asymmetric_keys/public_key.h +@@ -28,3 +28,9 @@ struct public_key_algorithm { + }; + + extern const struct public_key_algorithm RSA_public_key_algorithm; ++ ++/* ++ * public_key.c ++ */ ++extern int public_key_verify_signature(const struct public_key *pk, ++ const struct public_key_signature *sig); +diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c +index fac574c..8cb2f70 100644 +--- a/crypto/asymmetric_keys/x509_public_key.c ++++ b/crypto/asymmetric_keys/x509_public_key.c +@@ -76,7 +76,7 @@ static int x509_check_signature(const struct public_key *pub, + if (ret < 0) + goto error_mpi; + +- ret = pub->algo->verify_signature(pub, sig); ++ ret = public_key_verify_signature(pub, sig); + + pr_debug("Cert Verification: %d\n", ret); + +-- +1.8.1.4 + + +From da18477d1a1987dce0f3c5f78b62e5b223e2bf90 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:35 +0000 +Subject: [PATCH 08/47] KEYS: Store public key algo ID in public_key_signature + struct + +Store public key algorithm ID in public_key_signature struct for reference +purposes. This allows a public_key_signature struct to be embedded in +struct x509_certificate and struct pkcs7_message more easily. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +Reviewed-by: Josh Boyer +--- + include/crypto/public_key.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h +index 05778df..b34fda4 100644 +--- a/include/crypto/public_key.h ++++ b/include/crypto/public_key.h +@@ -90,6 +90,7 @@ struct public_key_signature { + u8 *digest; + u8 digest_size; /* Number of bytes in digest */ + u8 nr_mpi; /* Occupancy of mpi[] */ ++ enum pkey_algo pkey_algo : 8; + enum pkey_hash_algo pkey_hash_algo : 8; + union { + MPI mpi[2]; +-- +1.8.1.4 + + +From 29d80acc90a95ef5614cf36d4e30835bcc014cc4 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:35 +0000 +Subject: [PATCH 09/47] X.509: struct x509_certificate needs struct tm + declaring + +struct x509_certificate needs struct tm declaring by #inclusion of linux/time.h +prior to its definition. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +Reviewed-by: Josh Boyer +--- + crypto/asymmetric_keys/x509_parser.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h +index e583ad0..2d01182 100644 +--- a/crypto/asymmetric_keys/x509_parser.h ++++ b/crypto/asymmetric_keys/x509_parser.h +@@ -9,6 +9,7 @@ + * 2 of the Licence, or (at your option) any later version. + */ + ++#include + #include + + struct x509_certificate { +-- +1.8.1.4 + + +From ba3ba9e41abb17a7632075668e4f0a30edb59896 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:35 +0000 +Subject: [PATCH 10/47] X.509: Add bits needed for PKCS#7 + +PKCS#7 validation requires access to the serial number and the raw names in an +X.509 certificate. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +Reviewed-by: Josh Boyer +--- + crypto/asymmetric_keys/x509.asn1 | 2 +- + crypto/asymmetric_keys/x509_cert_parser.c | 17 +++++++++++++++++ + crypto/asymmetric_keys/x509_parser.h | 10 ++++++++-- + 3 files changed, 26 insertions(+), 3 deletions(-) + +diff --git a/crypto/asymmetric_keys/x509.asn1 b/crypto/asymmetric_keys/x509.asn1 +index bf32b3d..aae0cde 100644 +--- a/crypto/asymmetric_keys/x509.asn1 ++++ b/crypto/asymmetric_keys/x509.asn1 +@@ -6,7 +6,7 @@ Certificate ::= SEQUENCE { + + TBSCertificate ::= SEQUENCE { + version [ 0 ] Version DEFAULT, +- serialNumber CertificateSerialNumber, ++ serialNumber CertificateSerialNumber ({ x509_note_serial }), + signature AlgorithmIdentifier ({ x509_note_pkey_algo }), + issuer Name ({ x509_note_issuer }), + validity Validity, +diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c +index a583930..08bebf1 100644 +--- a/crypto/asymmetric_keys/x509_cert_parser.c ++++ b/crypto/asymmetric_keys/x509_cert_parser.c +@@ -209,6 +209,19 @@ int x509_note_signature(void *context, size_t hdrlen, + } + + /* ++ * Note the certificate serial number ++ */ ++int x509_note_serial(void *context, size_t hdrlen, ++ unsigned char tag, ++ const void *value, size_t vlen) ++{ ++ struct x509_parse_context *ctx = context; ++ ctx->cert->raw_serial = value; ++ ctx->cert->raw_serial_size = vlen; ++ return 0; ++} ++ ++/* + * Note some of the name segments from which we'll fabricate a name. + */ + int x509_extract_name_segment(void *context, size_t hdrlen, +@@ -320,6 +333,8 @@ int x509_note_issuer(void *context, size_t hdrlen, + const void *value, size_t vlen) + { + struct x509_parse_context *ctx = context; ++ ctx->cert->raw_issuer = value; ++ ctx->cert->raw_issuer_size = vlen; + return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen); + } + +@@ -328,6 +343,8 @@ int x509_note_subject(void *context, size_t hdrlen, + const void *value, size_t vlen) + { + struct x509_parse_context *ctx = context; ++ ctx->cert->raw_subject = value; ++ ctx->cert->raw_subject_size = vlen; + return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen); + } + +diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h +index 2d01182..a6ce46f 100644 +--- a/crypto/asymmetric_keys/x509_parser.h ++++ b/crypto/asymmetric_keys/x509_parser.h +@@ -24,9 +24,15 @@ struct x509_certificate { + enum pkey_algo sig_pkey_algo : 8; /* Signature public key algorithm */ + enum pkey_hash_algo sig_hash_algo : 8; /* Signature hash algorithm */ + const void *tbs; /* Signed data */ +- size_t tbs_size; /* Size of signed data */ ++ unsigned tbs_size; /* Size of signed data */ ++ unsigned sig_size; /* Size of sigature */ + const void *sig; /* Signature data */ +- size_t sig_size; /* Size of sigature */ ++ const void *raw_serial; /* Raw serial number in ASN.1 */ ++ unsigned raw_serial_size; ++ unsigned raw_issuer_size; ++ const void *raw_issuer; /* Raw issuer name in ASN.1 */ ++ const void *raw_subject; /* Raw subject name in ASN.1 */ ++ unsigned raw_subject_size; + }; + + /* +-- +1.8.1.4 + + +From 4d2f837ab3629d5b4b3bac2bbdbdf2d0060e74a8 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:36 +0000 +Subject: [PATCH 11/47] X.509: Embed public_key_signature struct and create + filler function + +Embed a public_key_signature struct in struct x509_certificate, eliminating +now unnecessary fields, and split x509_check_signature() to create a filler +function for it that attaches a digest of the signed data and an MPI that +represents the signature data. x509_free_certificate() is then modified to +deal with these. + +Whilst we're at it, export both x509_check_signature() and the new +x509_get_sig_params(). + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +Reviewed-by: Josh Boyer +--- + crypto/asymmetric_keys/x509_cert_parser.c | 30 +++++------ + crypto/asymmetric_keys/x509_parser.h | 14 ++++-- + crypto/asymmetric_keys/x509_public_key.c | 83 +++++++++++++++++-------------- + 3 files changed, 73 insertions(+), 54 deletions(-) + +diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c +index 08bebf1..931f069 100644 +--- a/crypto/asymmetric_keys/x509_cert_parser.c ++++ b/crypto/asymmetric_keys/x509_cert_parser.c +@@ -47,6 +47,8 @@ void x509_free_certificate(struct x509_certificate *cert) + kfree(cert->subject); + kfree(cert->fingerprint); + kfree(cert->authority); ++ kfree(cert->sig.digest); ++ mpi_free(cert->sig.rsa.s); + kfree(cert); + } + } +@@ -152,33 +154,33 @@ int x509_note_pkey_algo(void *context, size_t hdrlen, + return -ENOPKG; /* Unsupported combination */ + + case OID_md4WithRSAEncryption: +- ctx->cert->sig_hash_algo = PKEY_HASH_MD5; +- ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; ++ ctx->cert->sig.pkey_hash_algo = PKEY_HASH_MD5; ++ ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA; + break; + + case OID_sha1WithRSAEncryption: +- ctx->cert->sig_hash_algo = PKEY_HASH_SHA1; +- ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; ++ ctx->cert->sig.pkey_hash_algo = PKEY_HASH_SHA1; ++ ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA; + break; + + case OID_sha256WithRSAEncryption: +- ctx->cert->sig_hash_algo = PKEY_HASH_SHA256; +- ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; ++ ctx->cert->sig.pkey_hash_algo = PKEY_HASH_SHA256; ++ ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA; + break; + + case OID_sha384WithRSAEncryption: +- ctx->cert->sig_hash_algo = PKEY_HASH_SHA384; +- ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; ++ ctx->cert->sig.pkey_hash_algo = PKEY_HASH_SHA384; ++ ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA; + break; + + case OID_sha512WithRSAEncryption: +- ctx->cert->sig_hash_algo = PKEY_HASH_SHA512; +- ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; ++ ctx->cert->sig.pkey_hash_algo = PKEY_HASH_SHA512; ++ ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA; + break; + + case OID_sha224WithRSAEncryption: +- ctx->cert->sig_hash_algo = PKEY_HASH_SHA224; +- ctx->cert->sig_pkey_algo = PKEY_ALGO_RSA; ++ ctx->cert->sig.pkey_hash_algo = PKEY_HASH_SHA224; ++ ctx->cert->sig.pkey_algo = PKEY_ALGO_RSA; + break; + } + +@@ -203,8 +205,8 @@ int x509_note_signature(void *context, size_t hdrlen, + return -EINVAL; + } + +- ctx->cert->sig = value; +- ctx->cert->sig_size = vlen; ++ ctx->cert->raw_sig = value; ++ ctx->cert->raw_sig_size = vlen; + return 0; + } + +diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h +index a6ce46f..6b1d877 100644 +--- a/crypto/asymmetric_keys/x509_parser.h ++++ b/crypto/asymmetric_keys/x509_parser.h +@@ -21,18 +21,17 @@ struct x509_certificate { + char *authority; /* Authority key fingerprint as hex */ + struct tm valid_from; + struct tm valid_to; +- enum pkey_algo sig_pkey_algo : 8; /* Signature public key algorithm */ +- enum pkey_hash_algo sig_hash_algo : 8; /* Signature hash algorithm */ + const void *tbs; /* Signed data */ + unsigned tbs_size; /* Size of signed data */ +- unsigned sig_size; /* Size of sigature */ +- const void *sig; /* Signature data */ ++ unsigned raw_sig_size; /* Size of sigature */ ++ const void *raw_sig; /* Signature data */ + const void *raw_serial; /* Raw serial number in ASN.1 */ + unsigned raw_serial_size; + unsigned raw_issuer_size; + const void *raw_issuer; /* Raw issuer name in ASN.1 */ + const void *raw_subject; /* Raw subject name in ASN.1 */ + unsigned raw_subject_size; ++ struct public_key_signature sig; /* Signature parameters */ + }; + + /* +@@ -40,3 +39,10 @@ struct x509_certificate { + */ + extern void x509_free_certificate(struct x509_certificate *cert); + extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen); ++ ++/* ++ * x509_public_key.c ++ */ ++extern int x509_get_sig_params(struct x509_certificate *cert); ++extern int x509_check_signature(const struct public_key *pub, ++ struct x509_certificate *cert); +diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c +index 8cb2f70..b7c81d8 100644 +--- a/crypto/asymmetric_keys/x509_public_key.c ++++ b/crypto/asymmetric_keys/x509_public_key.c +@@ -24,72 +24,83 @@ + #include "x509_parser.h" + + /* +- * Check the signature on a certificate using the provided public key ++ * Set up the signature parameters in an X.509 certificate. This involves ++ * digesting the signed data and extracting the signature. + */ +-static int x509_check_signature(const struct public_key *pub, +- const struct x509_certificate *cert) ++int x509_get_sig_params(struct x509_certificate *cert) + { +- struct public_key_signature *sig; + struct crypto_shash *tfm; + struct shash_desc *desc; + size_t digest_size, desc_size; ++ void *digest; + int ret; + + pr_devel("==>%s()\n", __func__); +- ++ ++ if (cert->sig.rsa.s) ++ return 0; ++ ++ cert->sig.rsa.s = mpi_read_raw_data(cert->raw_sig, cert->raw_sig_size); ++ if (!cert->sig.rsa.s) ++ return -ENOMEM; ++ cert->sig.nr_mpi = 1; ++ + /* Allocate the hashing algorithm we're going to need and find out how + * big the hash operational data will be. + */ +- tfm = crypto_alloc_shash(pkey_hash_algo_name[cert->sig_hash_algo], 0, 0); ++ tfm = crypto_alloc_shash(pkey_hash_algo_name[cert->sig.pkey_hash_algo], 0, 0); + if (IS_ERR(tfm)) + return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + digest_size = crypto_shash_digestsize(tfm); + +- /* We allocate the hash operational data storage on the end of our +- * context data. ++ /* We allocate the hash operational data storage on the end of the ++ * digest storage space. + */ + ret = -ENOMEM; +- sig = kzalloc(sizeof(*sig) + desc_size + digest_size, GFP_KERNEL); +- if (!sig) +- goto error_no_sig; ++ digest = kzalloc(digest_size + desc_size, GFP_KERNEL); ++ if (!digest) ++ goto error; + +- sig->pkey_hash_algo = cert->sig_hash_algo; +- sig->digest = (u8 *)sig + sizeof(*sig) + desc_size; +- sig->digest_size = digest_size; ++ cert->sig.digest = digest; ++ cert->sig.digest_size = digest_size; + +- desc = (void *)sig + sizeof(*sig); +- desc->tfm = tfm; +- desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; ++ desc = digest + digest_size; ++ desc->tfm = tfm; ++ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; + + ret = crypto_shash_init(desc); + if (ret < 0) + goto error; ++ might_sleep(); ++ ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, digest); ++error: ++ crypto_free_shash(tfm); ++ pr_devel("<==%s() = %d\n", __func__, ret); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(x509_get_sig_params); + +- ret = -ENOMEM; +- sig->rsa.s = mpi_read_raw_data(cert->sig, cert->sig_size); +- if (!sig->rsa.s) +- goto error; ++/* ++ * Check the signature on a certificate using the provided public key ++ */ ++int x509_check_signature(const struct public_key *pub, ++ struct x509_certificate *cert) ++{ ++ int ret; + +- ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest); +- if (ret < 0) +- goto error_mpi; ++ pr_devel("==>%s()\n", __func__); + +- ret = public_key_verify_signature(pub, sig); ++ ret = x509_get_sig_params(cert); ++ if (ret < 0) ++ return ret; + ++ ret = public_key_verify_signature(pub, &cert->sig); + pr_debug("Cert Verification: %d\n", ret); +- +-error_mpi: +- mpi_free(sig->rsa.s); +-error: +- kfree(sig); +-error_no_sig: +- crypto_free_shash(tfm); +- +- pr_devel("<==%s() = %d\n", __func__, ret); + return ret; + } ++EXPORT_SYMBOL_GPL(x509_check_signature); + + /* + * Attempt to parse a data blob for a key as an X509 certificate. +@@ -118,8 +129,8 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) + cert->valid_to.tm_mday, cert->valid_to.tm_hour, + cert->valid_to.tm_min, cert->valid_to.tm_sec); + pr_devel("Cert Signature: %s + %s\n", +- pkey_algo_name[cert->sig_pkey_algo], +- pkey_hash_algo_name[cert->sig_hash_algo]); ++ pkey_algo_name[cert->sig.pkey_algo], ++ pkey_hash_algo_name[cert->sig.pkey_hash_algo]); + + if (!cert->fingerprint || !cert->authority) { + pr_warn("Cert for '%s' must have SubjKeyId and AuthKeyId extensions\n", +-- +1.8.1.4 + + +From 822175026ad1d4640240d1fdd77b1f45ddd9e7a9 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:36 +0000 +Subject: [PATCH 12/47] X.509: Check the algorithm IDs obtained from parsing an + X.509 certificate + +Check that the algorithm IDs obtained from the ASN.1 parse by OID lookup +corresponds to algorithms that are available to us. + +Reported-by: Kees Cook +Signed-off-by: David Howells +--- + crypto/asymmetric_keys/x509_public_key.c | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c +index b7c81d8..eb368d4 100644 +--- a/crypto/asymmetric_keys/x509_public_key.c ++++ b/crypto/asymmetric_keys/x509_public_key.c +@@ -119,6 +119,17 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) + + pr_devel("Cert Issuer: %s\n", cert->issuer); + pr_devel("Cert Subject: %s\n", cert->subject); ++ ++ if (cert->pub->pkey_algo >= PKEY_ALGO__LAST || ++ cert->sig.pkey_algo >= PKEY_ALGO__LAST || ++ cert->sig.pkey_hash_algo >= PKEY_HASH__LAST || ++ !pkey_algo[cert->pub->pkey_algo] || ++ !pkey_algo[cert->sig.pkey_algo] || ++ !pkey_hash_algo_name[cert->sig.pkey_hash_algo]) { ++ ret = -ENOPKG; ++ goto error_free_cert; ++ } ++ + pr_devel("Cert Key Algo: %s\n", pkey_algo_name[cert->pub->pkey_algo]); + pr_devel("Cert Valid From: %04ld-%02d-%02d %02d:%02d:%02d\n", + cert->valid_from.tm_year + 1900, cert->valid_from.tm_mon + 1, +-- +1.8.1.4 + + +From 4a1a540f79d36d8b0b8970ea638648cef080057b Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:37 +0000 +Subject: [PATCH 13/47] X.509: Handle certificates that lack an + authorityKeyIdentifier field + +Handle certificates that lack an authorityKeyIdentifier field by assuming +they're self-signed and checking their signatures against themselves. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +Reviewed-by: Josh Boyer +--- + crypto/asymmetric_keys/x509_public_key.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c +index eb368d4..0f55e3b 100644 +--- a/crypto/asymmetric_keys/x509_public_key.c ++++ b/crypto/asymmetric_keys/x509_public_key.c +@@ -143,8 +143,8 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) + pkey_algo_name[cert->sig.pkey_algo], + pkey_hash_algo_name[cert->sig.pkey_hash_algo]); + +- if (!cert->fingerprint || !cert->authority) { +- pr_warn("Cert for '%s' must have SubjKeyId and AuthKeyId extensions\n", ++ if (!cert->fingerprint) { ++ pr_warn("Cert for '%s' must have a SubjKeyId extension\n", + cert->subject); + ret = -EKEYREJECTED; + goto error_free_cert; +@@ -190,8 +190,9 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) + cert->pub->algo = pkey_algo[cert->pub->pkey_algo]; + cert->pub->id_type = PKEY_ID_X509; + +- /* Check the signature on the key */ +- if (strcmp(cert->fingerprint, cert->authority) == 0) { ++ /* Check the signature on the key if it appears to be self-signed */ ++ if (!cert->authority || ++ strcmp(cert->fingerprint, cert->authority) == 0) { + ret = x509_check_signature(cert->pub, cert); + if (ret < 0) + goto error_free_cert; +-- +1.8.1.4 + + +From f5e443e719cfb7cae2aea764ad3c9ec9ffba4f60 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:37 +0000 +Subject: [PATCH 14/47] X.509: Export certificate parse and free functions + +Export certificate parse and free functions for use by modules. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +Reviewed-by: Josh Boyer +--- + crypto/asymmetric_keys/x509_cert_parser.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c +index 931f069..9cf0e16 100644 +--- a/crypto/asymmetric_keys/x509_cert_parser.c ++++ b/crypto/asymmetric_keys/x509_cert_parser.c +@@ -11,6 +11,7 @@ + + #define pr_fmt(fmt) "X.509: "fmt + #include ++#include + #include + #include + #include +@@ -52,6 +53,7 @@ void x509_free_certificate(struct x509_certificate *cert) + kfree(cert); + } + } ++EXPORT_SYMBOL_GPL(x509_free_certificate); + + /* + * Parse an X.509 certificate +@@ -97,6 +99,7 @@ error_no_ctx: + error_no_cert: + return ERR_PTR(ret); + } ++EXPORT_SYMBOL_GPL(x509_cert_parse); + + /* + * Note an OID when we find one for later processing when we know how +-- +1.8.1.4 + + +From 792a56d205765cf4ece16868929ad5fbe6b89df4 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:38 +0000 +Subject: [PATCH 15/47] PKCS#7: Implement a parser [RFC 2315] + +Implement a parser for a PKCS#7 signed-data message as described in part of +RFC 2315. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +--- + crypto/asymmetric_keys/Kconfig | 9 + + crypto/asymmetric_keys/Makefile | 13 ++ + crypto/asymmetric_keys/pkcs7.asn1 | 127 +++++++++++++ + crypto/asymmetric_keys/pkcs7_parser.c | 326 ++++++++++++++++++++++++++++++++++ + crypto/asymmetric_keys/pkcs7_parser.h | 65 +++++++ + include/linux/oid_registry.h | 1 + + 6 files changed, 541 insertions(+) + create mode 100644 crypto/asymmetric_keys/pkcs7.asn1 + create mode 100644 crypto/asymmetric_keys/pkcs7_parser.c + create mode 100644 crypto/asymmetric_keys/pkcs7_parser.h + +diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig +index 6d2c2ea..413f3f6 100644 +--- a/crypto/asymmetric_keys/Kconfig ++++ b/crypto/asymmetric_keys/Kconfig +@@ -35,4 +35,13 @@ config X509_CERTIFICATE_PARSER + data and provides the ability to instantiate a crypto key from a + public key packet found inside the certificate. + ++config PKCS7_MESSAGE_PARSER ++ tristate "PKCS#7 message parser" ++ depends on X509_CERTIFICATE_PARSER ++ select ASN1 ++ select OID_REGISTRY ++ help ++ This option provides support for parsing PKCS#7 format messages for ++ signature data and provides the ability to verify the signature. ++ + endif # ASYMMETRIC_KEY_TYPE +diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile +index 0727204..59d8cad 100644 +--- a/crypto/asymmetric_keys/Makefile ++++ b/crypto/asymmetric_keys/Makefile +@@ -25,3 +25,16 @@ $(obj)/x509_rsakey-asn1.o: $(obj)/x509_rsakey-asn1.c $(obj)/x509_rsakey-asn1.h + + clean-files += x509-asn1.c x509-asn1.h + clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h ++ ++# ++# PKCS#7 message handling ++# ++obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o ++pkcs7_message-y := \ ++ pkcs7-asn1.o \ ++ pkcs7_parser.o ++ ++$(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h ++$(obj)/pkcs7-asn1.o: $(obj)/pkcs7-asn1.c $(obj)/pkcs7-asn1.h ++ ++clean-files += pkcs7-asn1.c pkcs7-asn1.h +diff --git a/crypto/asymmetric_keys/pkcs7.asn1 b/crypto/asymmetric_keys/pkcs7.asn1 +new file mode 100644 +index 0000000..7bf91ed +--- /dev/null ++++ b/crypto/asymmetric_keys/pkcs7.asn1 +@@ -0,0 +1,127 @@ ++PKCS7ContentInfo ::= SEQUENCE { ++ contentType ContentType, ++ content [0] EXPLICIT SignedData OPTIONAL ++} ++ ++ContentType ::= OBJECT IDENTIFIER ({ pkcs7_note_OID }) ++ ++SignedData ::= SEQUENCE { ++ version INTEGER, ++ digestAlgorithms DigestAlgorithmIdentifiers ({ pkcs7_note_digest_algo }), ++ contentInfo ContentInfo, ++ certificates CHOICE { ++ certSet [0] IMPLICIT ExtendedCertificatesAndCertificates, ++ certSequence [2] IMPLICIT Certificates ++ } OPTIONAL ({ pkcs7_note_certificate_list }), ++ crls CHOICE { ++ crlSet [1] IMPLICIT CertificateRevocationLists, ++ crlSequence [3] IMPLICIT CRLSequence ++ } OPTIONAL, ++ signerInfos SignerInfos ++} ++ ++ContentInfo ::= SEQUENCE { ++ contentType ContentType, ++ content [0] EXPLICIT Data OPTIONAL ++} ++ ++Data ::= ANY ({ pkcs7_note_data }) ++ ++DigestAlgorithmIdentifiers ::= CHOICE { ++ daSet SET OF DigestAlgorithmIdentifier, ++ daSequence SEQUENCE OF DigestAlgorithmIdentifier ++} ++ ++DigestAlgorithmIdentifier ::= SEQUENCE { ++ algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }), ++ parameters ANY OPTIONAL ++} ++ ++-- ++-- Certificates and certificate lists ++-- ++ExtendedCertificatesAndCertificates ::= SET OF ExtendedCertificateOrCertificate ++ ++ExtendedCertificateOrCertificate ::= CHOICE { ++ certificate Certificate, -- X.509 ++ extendedCertificate [0] IMPLICIT ExtendedCertificate -- PKCS#6 ++} ++ ++ExtendedCertificate ::= Certificate -- cheating ++ ++Certificates ::= SEQUENCE OF Certificate ++ ++CertificateRevocationLists ::= SET OF CertificateList ++ ++CertificateList ::= SEQUENCE OF Certificate -- This may be defined incorrectly ++ ++CRLSequence ::= SEQUENCE OF CertificateList ++ ++Certificate ::= ANY ({ pkcs7_extract_cert }) -- X.509 ++ ++-- ++-- Signer information ++-- ++SignerInfos ::= CHOICE { ++ siSet SET OF SignerInfo, ++ siSequence SEQUENCE OF SignerInfo ++} ++ ++SignerInfo ::= SEQUENCE { ++ version INTEGER, ++ issuerAndSerialNumber IssuerAndSerialNumber, ++ digestAlgorithm DigestAlgorithmIdentifier ({ pkcs7_note_digest_algo }), ++ authenticatedAttributes CHOICE { ++ aaSet [0] IMPLICIT SetOfAuthenticatedAttribute ++ ({ pkcs7_note_set_of_authattrs }), ++ aaSequence [2] EXPLICIT SEQUENCE OF AuthenticatedAttribute ++ -- Explicit because easier to compute digest on ++ -- sequence of attributes and then reuse encoded ++ -- sequence in aaSequence. ++ } OPTIONAL, ++ digestEncryptionAlgorithm ++ DigestEncryptionAlgorithmIdentifier ({ pkcs7_note_pkey_algo }), ++ encryptedDigest EncryptedDigest, ++ unauthenticatedAttributes CHOICE { ++ uaSet [1] IMPLICIT SET OF UnauthenticatedAttribute, ++ uaSequence [3] IMPLICIT SEQUENCE OF UnauthenticatedAttribute ++ } OPTIONAL ++} ++ ++IssuerAndSerialNumber ::= SEQUENCE { ++ issuer Name ({ pkcs7_note_issuer }), ++ serialNumber CertificateSerialNumber ({ pkcs7_note_serial }) ++} ++ ++CertificateSerialNumber ::= INTEGER ++ ++SetOfAuthenticatedAttribute ::= SET OF AuthenticatedAttribute ++ ++AuthenticatedAttribute ::= SEQUENCE { ++ type OBJECT IDENTIFIER ({ pkcs7_note_OID }), ++ values SET OF ANY ({ pkcs7_note_authenticated_attr }) ++} ++ ++UnauthenticatedAttribute ::= SEQUENCE { ++ type OBJECT IDENTIFIER ({ pkcs7_note_OID }), ++ values SET OF ANY ++} ++ ++DigestEncryptionAlgorithmIdentifier ::= SEQUENCE { ++ algorithm OBJECT IDENTIFIER ({ pkcs7_note_OID }), ++ parameters ANY OPTIONAL ++} ++ ++EncryptedDigest ::= OCTET STRING ({ pkcs7_note_signature }) ++ ++--- ++--- X.500 Name ++--- ++Name ::= SEQUENCE OF RelativeDistinguishedName ++ ++RelativeDistinguishedName ::= SET OF AttributeValueAssertion ++ ++AttributeValueAssertion ::= SEQUENCE { ++ attributeType OBJECT IDENTIFIER ({ pkcs7_note_OID }), ++ attributeValue ANY ++} +diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c +new file mode 100644 +index 0000000..231aff9 +--- /dev/null ++++ b/crypto/asymmetric_keys/pkcs7_parser.c +@@ -0,0 +1,326 @@ ++/* PKCS#7 parser ++ * ++ * Copyright (C) 2012 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) "PKCS7: "fmt ++#include ++#include ++#include ++#include ++#include ++#include "public_key.h" ++#include "pkcs7_parser.h" ++#include "pkcs7-asn1.h" ++ ++struct pkcs7_parse_context { ++ struct pkcs7_message *msg; /* Message being constructed */ ++ struct x509_certificate *certs; /* Certificate cache */ ++ struct x509_certificate **ppcerts; ++ unsigned long data; /* Start of data */ ++ enum OID last_oid; /* Last OID encountered */ ++}; ++ ++/* ++ * Free a PKCS#7 message ++ */ ++void pkcs7_free_message(struct pkcs7_message *pkcs7) ++{ ++ struct x509_certificate *cert; ++ ++ if (pkcs7) { ++ while (pkcs7->certs) { ++ cert = pkcs7->certs; ++ pkcs7->certs = cert->next; ++ x509_free_certificate(cert); ++ } ++ while (pkcs7->crl) { ++ cert = pkcs7->crl; ++ pkcs7->crl = cert->next; ++ x509_free_certificate(cert); ++ } ++ kfree(pkcs7->sig.digest); ++ mpi_free(pkcs7->sig.mpi[0]); ++ kfree(pkcs7); ++ } ++} ++EXPORT_SYMBOL_GPL(pkcs7_free_message); ++ ++/* ++ * Parse a PKCS#7 message ++ */ ++struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) ++{ ++ struct pkcs7_parse_context *ctx; ++ struct pkcs7_message *msg; ++ long ret; ++ ++ ret = -ENOMEM; ++ msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL); ++ if (!msg) ++ goto error_no_sig; ++ ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL); ++ if (!ctx) ++ goto error_no_ctx; ++ ++ ctx->msg = msg; ++ ctx->data = (unsigned long)data; ++ ctx->ppcerts = &ctx->certs; ++ ++ /* Attempt to decode the signature */ ++ ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen); ++ if (ret < 0) ++ goto error_decode; ++ ++ while (ctx->certs) { ++ struct x509_certificate *cert = ctx->certs; ++ ctx->certs = cert->next; ++ x509_free_certificate(cert); ++ } ++ kfree(ctx); ++ return msg; ++ ++error_decode: ++ kfree(ctx); ++error_no_ctx: ++ pkcs7_free_message(msg); ++error_no_sig: ++ return ERR_PTR(ret); ++} ++EXPORT_SYMBOL_GPL(pkcs7_parse_message); ++ ++/* ++ * Note an OID when we find one for later processing when we know how ++ * to interpret it. ++ */ ++int pkcs7_note_OID(void *context, size_t hdrlen, ++ unsigned char tag, ++ const void *value, size_t vlen) ++{ ++ struct pkcs7_parse_context *ctx = context; ++ ++ ctx->last_oid = look_up_OID(value, vlen); ++ if (ctx->last_oid == OID__NR) { ++ char buffer[50]; ++ sprint_oid(value, vlen, buffer, sizeof(buffer)); ++ printk("PKCS7: Unknown OID: [%lu] %s\n", ++ (unsigned long)value - ctx->data, buffer); ++ } ++ return 0; ++} ++ ++/* ++ * Note the digest algorithm for the signature. ++ */ ++int pkcs7_note_digest_algo(void *context, size_t hdrlen, ++ unsigned char tag, ++ const void *value, size_t vlen) ++{ ++ struct pkcs7_parse_context *ctx = context; ++ ++ switch (ctx->last_oid) { ++ case OID_md4: ++ ctx->msg->sig.pkey_hash_algo = PKEY_HASH_MD4; ++ break; ++ case OID_md5: ++ ctx->msg->sig.pkey_hash_algo = PKEY_HASH_MD5; ++ break; ++ case OID_sha1: ++ ctx->msg->sig.pkey_hash_algo = PKEY_HASH_SHA1; ++ break; ++ case OID_sha256: ++ ctx->msg->sig.pkey_hash_algo = PKEY_HASH_SHA256; ++ break; ++ default: ++ printk("Unsupported digest algo: %u\n", ctx->last_oid); ++ return -ENOPKG; ++ } ++ return 0; ++} ++ ++/* ++ * Note the public key algorithm for the signature. ++ */ ++int pkcs7_note_pkey_algo(void *context, size_t hdrlen, ++ unsigned char tag, ++ const void *value, size_t vlen) ++{ ++ struct pkcs7_parse_context *ctx = context; ++ ++ switch (ctx->last_oid) { ++ case OID_rsaEncryption: ++ ctx->msg->sig.pkey_algo = PKEY_ALGO_RSA; ++ break; ++ default: ++ printk("Unsupported pkey algo: %u\n", ctx->last_oid); ++ return -ENOPKG; ++ } ++ return 0; ++} ++ ++/* ++ * Extract a certificate and store it in the context. ++ */ ++int pkcs7_extract_cert(void *context, size_t hdrlen, ++ unsigned char tag, ++ const void *value, size_t vlen) ++{ ++ struct pkcs7_parse_context *ctx = context; ++ struct x509_certificate *cert; ++ ++ if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) { ++ pr_debug("Cert began with tag %02x at %lu\n", ++ tag, (unsigned long)ctx - ctx->data); ++ return -EBADMSG; ++ } ++ ++ /* We have to correct for the header so that the X.509 parser can start ++ * from the beginning. Note that since X.509 stipulates DER, there ++ * probably shouldn't be an EOC trailer - but it is in PKCS#7 (which ++ * stipulates BER). ++ */ ++ value -= hdrlen; ++ vlen += hdrlen; ++ ++ if (((u8*)value)[1] == 0x80) ++ vlen += 2; /* Indefinite length - there should be an EOC */ ++ ++ cert = x509_cert_parse(value, vlen); ++ if (IS_ERR(cert)) ++ return PTR_ERR(cert); ++ ++ pr_debug("Got cert for %s\n", cert->subject); ++ pr_debug("- fingerprint %s\n", cert->fingerprint); ++ ++ *ctx->ppcerts = cert; ++ ctx->ppcerts = &cert->next; ++ return 0; ++} ++ ++/* ++ * Save the certificate list ++ */ ++int pkcs7_note_certificate_list(void *context, size_t hdrlen, ++ unsigned char tag, ++ const void *value, size_t vlen) ++{ ++ struct pkcs7_parse_context *ctx = context; ++ ++ pr_devel("Got cert list (%02x)\n", tag); ++ ++ *ctx->ppcerts = ctx->msg->certs; ++ ctx->msg->certs = ctx->certs; ++ ctx->certs = NULL; ++ ctx->ppcerts = &ctx->certs; ++ return 0; ++} ++ ++/* ++ * Extract the data from the signature and store that and its content type OID ++ * in the context. ++ */ ++int pkcs7_note_data(void *context, size_t hdrlen, ++ unsigned char tag, ++ const void *value, size_t vlen) ++{ ++ struct pkcs7_parse_context *ctx = context; ++ ++ pr_debug("Got data\n"); ++ ++ ctx->msg->data = value; ++ ctx->msg->data_len = vlen; ++ ctx->msg->data_hdrlen = hdrlen; ++ ctx->msg->data_type = ctx->last_oid; ++ return 0; ++} ++ ++/* ++ * Parse authenticated attributes ++ */ ++int pkcs7_note_authenticated_attr(void *context, size_t hdrlen, ++ unsigned char tag, ++ const void *value, size_t vlen) ++{ ++ struct pkcs7_parse_context *ctx = context; ++ ++ pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value); ++ ++ switch (ctx->last_oid) { ++ case OID_messageDigest: ++ if (tag != ASN1_OTS) ++ return -EBADMSG; ++ ctx->msg->msgdigest = value; ++ ctx->msg->msgdigest_len = vlen; ++ return 0; ++ default: ++ return 0; ++ } ++} ++ ++/* ++ * Note the set of auth attributes for digestion purposes [RFC2315 9.3] ++ */ ++int pkcs7_note_set_of_authattrs(void *context, size_t hdrlen, ++ unsigned char tag, ++ const void *value, size_t vlen) ++{ ++ struct pkcs7_parse_context *ctx = context; ++ ++ /* We need to switch the 'CONT 0' to a 'SET OF' when we digest */ ++ ctx->msg->authattrs = value - (hdrlen - 1); ++ ctx->msg->authattrs_len = vlen + (hdrlen - 1); ++ return 0; ++} ++ ++/* ++ * Note the issuing certificate serial number ++ */ ++int pkcs7_note_serial(void *context, size_t hdrlen, ++ unsigned char tag, ++ const void *value, size_t vlen) ++{ ++ struct pkcs7_parse_context *ctx = context; ++ ctx->msg->raw_serial = value; ++ ctx->msg->raw_serial_size = vlen; ++ return 0; ++} ++ ++/* ++ * Note the issuer's name ++ */ ++int pkcs7_note_issuer(void *context, size_t hdrlen, ++ unsigned char tag, ++ const void *value, size_t vlen) ++{ ++ struct pkcs7_parse_context *ctx = context; ++ ctx->msg->raw_issuer = value; ++ ctx->msg->raw_issuer_size = vlen; ++ return 0; ++} ++ ++/* ++ * Note the signature data ++ */ ++int pkcs7_note_signature(void *context, size_t hdrlen, ++ unsigned char tag, ++ const void *value, size_t vlen) ++{ ++ struct pkcs7_parse_context *ctx = context; ++ MPI mpi; ++ ++ BUG_ON(ctx->msg->sig.pkey_algo != PKEY_ALGO_RSA); ++ ++ mpi = mpi_read_raw_data(value, vlen); ++ if (!mpi) ++ return -ENOMEM; ++ ++ ctx->msg->sig.mpi[0] = mpi; ++ ctx->msg->sig.nr_mpi = 1; ++ return 0; ++} +diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h +new file mode 100644 +index 0000000..5415857 +--- /dev/null ++++ b/crypto/asymmetric_keys/pkcs7_parser.h +@@ -0,0 +1,65 @@ ++/* PKCS#7 crypto data parser internal definitions ++ * ++ * Copyright (C) 2012 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. ++ */ ++ ++#include ++#include "x509_parser.h" ++ ++#define kenter(FMT, ...) \ ++ pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__) ++#define kleave(FMT, ...) \ ++ pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__) ++ ++struct pkcs7_message { ++ struct x509_certificate *certs; /* Certificate list */ ++ struct x509_certificate *crl; /* Revocation list */ ++ struct x509_certificate *signer; /* Signing certificate (in ->certs) */ ++ ++ /* Content Data (or NULL) */ ++ enum OID data_type; /* Type of Data */ ++ size_t data_len; /* Length of Data */ ++ size_t data_hdrlen; /* Length of Data ASN.1 header */ ++ const void *data; /* Content Data (or 0) */ ++ ++ /* Message digest - the digest of the Content Data (or NULL) */ ++ const void *msgdigest; ++ unsigned msgdigest_len; ++ ++ /* Authenticated Attribute data (or NULL) */ ++ unsigned authattrs_len; ++ const void *authattrs; ++ ++ /* Issuing cert serial number and issuer's name */ ++ const void *raw_serial; ++ unsigned raw_serial_size; ++ unsigned raw_issuer_size; ++ const void *raw_issuer; ++ ++ /* Message signature. ++ * ++ * This contains the generated digest of _either_ the Content Data or ++ * the Authenticated Attributes [RFC2315 9.3]. If the latter, one of ++ * the attributes contains the digest of the the Content Data within ++ * it. ++ */ ++ struct public_key_signature sig; ++}; ++ ++/* ++ * pkcs7_parser.c ++ */ ++extern struct pkcs7_message *pkcs7_parse_message(const void *data, ++ size_t datalen); ++extern void pkcs7_free_message(struct pkcs7_message *pkcs7); ++ ++/* ++ * pkcs7_verify.c ++ */ ++extern int pkcs7_verify(struct pkcs7_message *pkcs7); +diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h +index 6926db7..edeff85 100644 +--- a/include/linux/oid_registry.h ++++ b/include/linux/oid_registry.h +@@ -55,6 +55,7 @@ enum OID { + OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */ + OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */ + OID_sha1, /* 1.3.14.3.2.26 */ ++ OID_sha256, /* 2.16.840.1.101.3.4.2.1 */ + + /* Distinguished Name attribute IDs [RFC 2256] */ + OID_commonName, /* 2.5.4.3 */ +-- +1.8.1.4 + + +From 3b4b82eecde52c1bd75ab11ef7f8a5c13ec73c40 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:38 +0000 +Subject: [PATCH 16/47] PKCS#7: Digest the data in a signed-data message + +Digest the data in a PKCS#7 signed-data message and attach to the +public_key_signature struct contained in the pkcs7_message struct. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +--- + crypto/asymmetric_keys/Makefile | 3 +- + crypto/asymmetric_keys/pkcs7_verify.c | 134 ++++++++++++++++++++++++++++++++++ + 2 files changed, 136 insertions(+), 1 deletion(-) + create mode 100644 crypto/asymmetric_keys/pkcs7_verify.c + +diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile +index 59d8cad..b6b39e7 100644 +--- a/crypto/asymmetric_keys/Makefile ++++ b/crypto/asymmetric_keys/Makefile +@@ -32,7 +32,8 @@ clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h + obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o + pkcs7_message-y := \ + pkcs7-asn1.o \ +- pkcs7_parser.o ++ pkcs7_parser.o \ ++ pkcs7_verify.o + + $(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h + $(obj)/pkcs7-asn1.o: $(obj)/pkcs7-asn1.c $(obj)/pkcs7-asn1.h +diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c +new file mode 100644 +index 0000000..2f9f26c +--- /dev/null ++++ b/crypto/asymmetric_keys/pkcs7_verify.c +@@ -0,0 +1,134 @@ ++/* Verify the signature on a PKCS#7 message. ++ * ++ * Copyright (C) 2012 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) "PKCS7: "fmt ++#include ++#include ++#include ++#include ++#include ++#include ++#include "public_key.h" ++#include "pkcs7_parser.h" ++ ++/* ++ * Digest the relevant parts of the PKCS#7 data ++ */ ++static int pkcs7_digest(struct pkcs7_message *pkcs7) ++{ ++ struct crypto_shash *tfm; ++ struct shash_desc *desc; ++ size_t digest_size, desc_size; ++ void *digest; ++ int ret; ++ ++ kenter(",%u", pkcs7->sig.pkey_hash_algo); ++ ++ if (pkcs7->sig.pkey_hash_algo >= PKEY_HASH__LAST || ++ !pkey_hash_algo_name[pkcs7->sig.pkey_hash_algo]) ++ return -ENOPKG; ++ ++ /* Allocate the hashing algorithm we're going to need and find out how ++ * big the hash operational data will be. ++ */ ++ tfm = crypto_alloc_shash(pkey_hash_algo_name[pkcs7->sig.pkey_hash_algo], ++ 0, 0); ++ if (IS_ERR(tfm)) ++ return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); ++ ++ desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); ++ pkcs7->sig.digest_size = digest_size = crypto_shash_digestsize(tfm); ++ ++ ret = -ENOMEM; ++ digest = kzalloc(digest_size + desc_size, GFP_KERNEL); ++ if (!digest) ++ goto error_no_desc; ++ ++ desc = digest + digest_size; ++ desc->tfm = tfm; ++ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; ++ ++ /* Digest the message [RFC2315 9.3] */ ++ ret = crypto_shash_init(desc); ++ if (ret < 0) ++ goto error; ++ ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, digest); ++ if (ret < 0) ++ goto error; ++ pr_devel("MsgDigest = [%*ph]\n", 8, digest); ++ ++ /* However, if there are authenticated attributes, there must be a ++ * message digest attribute amongst them which corresponds to the ++ * digest we just calculated. ++ */ ++ if (pkcs7->msgdigest) { ++ u8 tag; ++ ++ if (pkcs7->msgdigest_len != pkcs7->sig.digest_size) { ++ pr_debug("Invalid digest size (%u)\n", ++ pkcs7->msgdigest_len); ++ ret = -EBADMSG; ++ goto error; ++ } ++ ++ if (memcmp(digest, pkcs7->msgdigest, pkcs7->msgdigest_len) != 0) { ++ pr_debug("Message digest doesn't match\n"); ++ ret = -EKEYREJECTED; ++ goto error; ++ } ++ ++ /* We then calculate anew, using the authenticated attributes ++ * as the contents of the digest instead. Note that we need to ++ * convert the attributes from a CONT.0 into a SET before we ++ * hash it. ++ */ ++ memset(digest, 0, pkcs7->sig.digest_size); ++ ++ ret = crypto_shash_init(desc); ++ if (ret < 0) ++ goto error; ++ tag = ASN1_CONS_BIT | ASN1_SET; ++ ret = crypto_shash_update(desc, &tag, 1); ++ if (ret < 0) ++ goto error; ++ ret = crypto_shash_finup(desc, pkcs7->authattrs, ++ pkcs7->authattrs_len, digest); ++ if (ret < 0) ++ goto error; ++ pr_devel("AADigest = [%*ph]\n", 8, digest); ++ } ++ ++ pkcs7->sig.digest = digest; ++ digest = NULL; ++ ++error: ++ kfree(digest); ++error_no_desc: ++ crypto_free_shash(tfm); ++ kleave(" = %d\n", ret); ++ return ret; ++} ++ ++/* ++ * Verify a PKCS#7 message ++ */ ++int pkcs7_verify(struct pkcs7_message *pkcs7) ++{ ++ int ret; ++ ++ /* First of all, digest the data in the PKCS#7 message */ ++ ret = pkcs7_digest(pkcs7); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(pkcs7_verify); +-- +1.8.1.4 + + +From e67fed4626a30dd11967abad9187013ff4185991 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:39 +0000 +Subject: [PATCH 17/47] PKCS#7: Find the right key in the PKCS#7 key list and + verify the signature + +Find the appropriate key in the PKCS#7 key list and verify the signature with +it. There may be several keys in there forming a chain. Any link in that +chain or the root of that chain may be in our keyrings. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +--- + crypto/asymmetric_keys/pkcs7_verify.c | 61 +++++++++++++++++++++++++++++++++++ + 1 file changed, 61 insertions(+) + +diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c +index 2f9f26c..3f6f0e2 100644 +--- a/crypto/asymmetric_keys/pkcs7_verify.c ++++ b/crypto/asymmetric_keys/pkcs7_verify.c +@@ -118,6 +118,53 @@ error_no_desc: + } + + /* ++ * Find the key (X.509 certificate) to use to verify a PKCS#7 message. PKCS#7 ++ * uses the issuer's name and the issuing certificate serial number for ++ * matching purposes. These must match the certificate issuer's name (not ++ * subject's name) and the certificate serial number [RFC 2315 6.7]. ++ */ ++static int pkcs7_find_key(struct pkcs7_message *pkcs7) ++{ ++ struct x509_certificate *x509; ++ ++ kenter("%u,%u", pkcs7->raw_serial_size, pkcs7->raw_issuer_size); ++ ++ for (x509 = pkcs7->certs; x509; x509 = x509->next) { ++ pr_devel("- x509 %u,%u\n", ++ x509->raw_serial_size, x509->raw_issuer_size); ++ ++ /* I'm _assuming_ that the generator of the PKCS#7 message will ++ * encode the fields from the X.509 cert in the same way in the ++ * PKCS#7 message - but I can't be 100% sure of that. It's ++ * possible this will need element-by-element comparison. ++ */ ++ if (x509->raw_serial_size != pkcs7->raw_serial_size || ++ memcmp(x509->raw_serial, pkcs7->raw_serial, ++ pkcs7->raw_serial_size) != 0) ++ continue; ++ pr_devel("Found cert serial match\n"); ++ ++ if (x509->raw_issuer_size != pkcs7->raw_issuer_size || ++ memcmp(x509->raw_issuer, pkcs7->raw_issuer, ++ pkcs7->raw_issuer_size) != 0) { ++ pr_warn("X.509 subject and PKCS#7 issuer don't match\n"); ++ continue; ++ } ++ ++ if (x509->pub->pkey_algo != pkcs7->sig.pkey_algo) { ++ pr_warn("X.509 algo and PKCS#7 sig algo don't match\n"); ++ continue; ++ } ++ ++ pkcs7->signer = x509; ++ return 0; ++ } ++ pr_warn("Issuing X.509 cert not found (#%*ph)\n", ++ pkcs7->raw_serial_size, pkcs7->raw_serial); ++ return -ENOKEY; ++} ++ ++/* + * Verify a PKCS#7 message + */ + int pkcs7_verify(struct pkcs7_message *pkcs7) +@@ -129,6 +176,20 @@ int pkcs7_verify(struct pkcs7_message *pkcs7) + if (ret < 0) + return ret; + ++ /* Find the key for the message signature */ ++ ret = pkcs7_find_key(pkcs7); ++ if (ret < 0) ++ return ret; ++ ++ pr_devel("Found X.509 cert\n"); ++ ++ /* Verify the PKCS#7 binary against the key */ ++ ret = public_key_verify_signature(pkcs7->signer->pub, &pkcs7->sig); ++ if (ret < 0) ++ return ret; ++ ++ pr_devel("Verified signature\n"); ++ + return 0; + } + EXPORT_SYMBOL_GPL(pkcs7_verify); +-- +1.8.1.4 + + +From 87ec8d783c887617ee6e85f66a9ce1a03c627e87 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:39 +0000 +Subject: [PATCH 18/47] PKCS#7: Verify internal certificate chain + +Verify certificate chain in the X.509 certificates contained within the PKCS#7 +message as far as possible. If any signature that we should be able to verify +fails, we reject the whole lot. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +--- + crypto/asymmetric_keys/pkcs7_verify.c | 67 ++++++++++++++++++++++++++++++++++- + crypto/asymmetric_keys/x509_parser.h | 1 + + 2 files changed, 67 insertions(+), 1 deletion(-) + +diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c +index 3f6f0e2..b3774bd 100644 +--- a/crypto/asymmetric_keys/pkcs7_verify.c ++++ b/crypto/asymmetric_keys/pkcs7_verify.c +@@ -165,6 +165,70 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7) + } + + /* ++ * Verify the internal certificate chain as best we can. ++ */ ++static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7) ++{ ++ struct x509_certificate *x509 = pkcs7->signer, *p; ++ int ret; ++ ++ kenter(""); ++ ++ for (;;) { ++ pr_debug("verify %s: %s\n", x509->subject, x509->fingerprint); ++ ret = x509_get_sig_params(x509); ++ if (ret < 0) ++ return ret; ++ ++ if (x509->issuer) ++ pr_debug("- issuer %s\n", x509->issuer); ++ if (x509->authority) ++ pr_debug("- authkeyid %s\n", x509->authority); ++ ++ if (!x509->authority || ++ (x509->subject && ++ strcmp(x509->subject, x509->authority) == 0)) { ++ /* If there's no authority certificate specified, then ++ * the certificate must be self-signed and is the root ++ * of the chain. Likewise if the cert is its own ++ * authority. ++ */ ++ pr_debug("- no auth?\n"); ++ if (x509->raw_subject_size != x509->raw_issuer_size || ++ memcmp(x509->raw_subject, x509->raw_issuer, ++ x509->raw_issuer_size) != 0) ++ return 0; ++ ++ ret = x509_check_signature(x509->pub, x509); ++ if (ret < 0) ++ return ret; ++ x509->signer = x509; ++ pr_debug("- self-signed\n"); ++ return 0; ++ } ++ ++ for (p = pkcs7->certs; p; p = p->next) ++ if (!p->signer && ++ p->raw_subject_size == x509->raw_issuer_size && ++ strcmp(p->fingerprint, x509->authority) == 0 && ++ memcmp(p->raw_subject, x509->raw_issuer, ++ x509->raw_issuer_size) == 0) ++ goto found_issuer; ++ pr_debug("- top\n"); ++ return 0; ++ ++ found_issuer: ++ pr_debug("- issuer %s\n", p->subject); ++ ret = x509_check_signature(p->pub, x509); ++ if (ret < 0) ++ return ret; ++ x509->signer = p; ++ x509 = p; ++ might_sleep(); ++ } ++} ++ ++/* + * Verify a PKCS#7 message + */ + int pkcs7_verify(struct pkcs7_message *pkcs7) +@@ -190,6 +254,7 @@ int pkcs7_verify(struct pkcs7_message *pkcs7) + + pr_devel("Verified signature\n"); + +- return 0; ++ /* Verify the internal certificate chain */ ++ return pkcs7_verify_sig_chain(pkcs7); + } + EXPORT_SYMBOL_GPL(pkcs7_verify); +diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h +index 6b1d877..5e35fba 100644 +--- a/crypto/asymmetric_keys/x509_parser.h ++++ b/crypto/asymmetric_keys/x509_parser.h +@@ -14,6 +14,7 @@ + + struct x509_certificate { + struct x509_certificate *next; ++ const struct x509_certificate *signer; /* Certificate that signed this one */ + struct public_key *pub; /* Public key details */ + char *issuer; /* Name of certificate issuer */ + char *subject; /* Name of certificate subject */ +-- +1.8.1.4 + + +From cc6c40318a05330e4bb201b35378d7c0a0278aaa Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:42 +0000 +Subject: [PATCH 19/47] PKCS#7: Find intersection between PKCS#7 message and + known, trusted keys + +Find the intersection between the X.509 certificate chain contained in a PKCS#7 +message and a set of keys that we already know and trust. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +--- + crypto/asymmetric_keys/Makefile | 1 + + crypto/asymmetric_keys/pkcs7_parser.h | 7 ++ + crypto/asymmetric_keys/pkcs7_trust.c | 149 ++++++++++++++++++++++++++++++++++ + 3 files changed, 157 insertions(+) + create mode 100644 crypto/asymmetric_keys/pkcs7_trust.c + +diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile +index b6b39e7..d63cb43 100644 +--- a/crypto/asymmetric_keys/Makefile ++++ b/crypto/asymmetric_keys/Makefile +@@ -33,6 +33,7 @@ obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o + pkcs7_message-y := \ + pkcs7-asn1.o \ + pkcs7_parser.o \ ++ pkcs7_trust.o \ + pkcs7_verify.o + + $(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h +diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h +index 5415857..ffa72dc 100644 +--- a/crypto/asymmetric_keys/pkcs7_parser.h ++++ b/crypto/asymmetric_keys/pkcs7_parser.h +@@ -60,6 +60,13 @@ extern struct pkcs7_message *pkcs7_parse_message(const void *data, + extern void pkcs7_free_message(struct pkcs7_message *pkcs7); + + /* ++ * pkcs7_trust.c ++ */ ++extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, ++ struct key *trust_keyring, ++ bool *_trusted); ++ ++/* + * pkcs7_verify.c + */ + extern int pkcs7_verify(struct pkcs7_message *pkcs7); +diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c +new file mode 100644 +index 0000000..cc226f5 +--- /dev/null ++++ b/crypto/asymmetric_keys/pkcs7_trust.c +@@ -0,0 +1,149 @@ ++/* Validate the trust chain of a PKCS#7 message. ++ * ++ * Copyright (C) 2012 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) "PKCS7: "fmt ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "public_key.h" ++#include "pkcs7_parser.h" ++ ++/* ++ * Request an asymmetric key. ++ */ ++static struct key *pkcs7_request_asymmetric_key( ++ struct key *keyring, ++ const char *signer, size_t signer_len, ++ const char *authority, size_t auth_len) ++{ ++ key_ref_t key; ++ char *id; ++ ++ kenter(",%zu,,%zu", signer_len, auth_len); ++ ++ /* Construct an identifier. */ ++ id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL); ++ if (!id) ++ return ERR_PTR(-ENOMEM); ++ ++ memcpy(id, signer, signer_len); ++ id[signer_len + 0] = ':'; ++ id[signer_len + 1] = ' '; ++ memcpy(id + signer_len + 2, authority, auth_len); ++ id[signer_len + 2 + auth_len] = 0; ++ ++ pr_debug("Look up: \"%s\"\n", id); ++ ++ key = keyring_search(make_key_ref(keyring, 1), ++ &key_type_asymmetric, id); ++ if (IS_ERR(key)) ++ pr_debug("Request for module key '%s' err %ld\n", ++ id, PTR_ERR(key)); ++ kfree(id); ++ ++ if (IS_ERR(key)) { ++ switch (PTR_ERR(key)) { ++ /* Hide some search errors */ ++ case -EACCES: ++ case -ENOTDIR: ++ case -EAGAIN: ++ return ERR_PTR(-ENOKEY); ++ default: ++ return ERR_CAST(key); ++ } ++ } ++ ++ pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key_ref_to_ptr(key))); ++ return key_ref_to_ptr(key); ++} ++ ++/* ++ * Validate that the certificate chain inside the PKCS#7 message intersects ++ * keys we already know and trust. ++ */ ++int pkcs7_validate_trust(struct pkcs7_message *pkcs7, ++ struct key *trust_keyring, ++ bool *_trusted) ++{ ++ struct public_key_signature *sig = &pkcs7->sig; ++ struct x509_certificate *x509, *last = NULL; ++ struct key *key; ++ bool trusted; ++ int ret; ++ ++ kenter(""); ++ ++ for (x509 = pkcs7->signer; x509; x509 = x509->next) { ++ /* Look to see if this certificate is present in the trusted ++ * keys. ++ */ ++ key = pkcs7_request_asymmetric_key( ++ trust_keyring, ++ x509->subject, strlen(x509->subject), ++ x509->fingerprint, strlen(x509->fingerprint)); ++ if (!IS_ERR(key)) ++ /* One of the X.509 certificates in the PKCS#7 message ++ * is apparently the same as one we already trust. ++ * Verify that the trusted variant can also validate ++ * the signature on the descendent. ++ */ ++ goto matched; ++ if (key == ERR_PTR(-ENOMEM)) ++ return -ENOMEM; ++ ++ /* Self-signed certificates form roots of their own, and if we ++ * don't know them, then we can't accept them. ++ */ ++ if (x509->next == x509) { ++ kleave(" = -EKEYREJECTED [unknown self-signed]"); ++ return -EKEYREJECTED; ++ } ++ ++ might_sleep(); ++ last = x509; ++ sig = &last->sig; ++ } ++ ++ /* No match - see if the root certificate has a signer amongst the ++ * trusted keys. ++ */ ++ if (!last || !last->issuer || !last->authority) { ++ kleave(" = -EKEYREJECTED [no backref]"); ++ return -EKEYREJECTED; ++ } ++ ++ key = pkcs7_request_asymmetric_key( ++ trust_keyring, ++ last->issuer, strlen(last->issuer), ++ last->authority, strlen(last->authority)); ++ if (IS_ERR(key)) ++ return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -EKEYREJECTED; ++ ++matched: ++ ret = verify_signature(key, sig); ++ trusted = test_bit(KEY_FLAG_TRUSTED, &key->flags); ++ key_put(key); ++ if (ret < 0) { ++ if (ret == -ENOMEM) ++ return ret; ++ kleave(" = -EKEYREJECTED [verify %d]", ret); ++ return -EKEYREJECTED; ++ } ++ ++ *_trusted = trusted; ++ kleave(" = 0"); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(pkcs7_validate_trust); +-- +1.8.1.4 + + +From f20b0d77771133bd0d7e89932fef494f00687607 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:39 +0000 +Subject: [PATCH 20/47] Provide PE binary definitions + +Provide some PE binary structural and constant definitions as taken from the +pesign package sources. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +--- + include/linux/pe.h | 448 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 448 insertions(+) + create mode 100644 include/linux/pe.h + +diff --git a/include/linux/pe.h b/include/linux/pe.h +new file mode 100644 +index 0000000..9234aef +--- /dev/null ++++ b/include/linux/pe.h +@@ -0,0 +1,448 @@ ++/* ++ * Copyright 2011 Red Hat, Inc. ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ * ++ * Author(s): Peter Jones ++ */ ++#ifndef __LINUX_PE_H ++#define __LINUX_PE_H ++ ++#include ++ ++#define MZ_MAGIC 0x5a4d /* "MZ" */ ++ ++struct mz_hdr { ++ uint16_t magic; /* MZ_MAGIC */ ++ uint16_t lbsize; /* size of last used block */ ++ uint16_t blocks; /* pages in file, 0x3 */ ++ uint16_t relocs; /* relocations */ ++ uint16_t hdrsize; /* header size in "paragraphs" */ ++ uint16_t min_extra_pps; /* .bss */ ++ uint16_t max_extra_pps; /* runtime limit for the arena size */ ++ uint16_t ss; /* relative stack segment */ ++ uint16_t sp; /* initial %sp register */ ++ uint16_t checksum; /* word checksum */ ++ uint16_t ip; /* initial %ip register */ ++ uint16_t cs; /* initial %cs relative to load segment */ ++ uint16_t reloc_table_offset; /* offset of the first relocation */ ++ uint16_t overlay_num; /* overlay number. set to 0. */ ++ uint16_t reserved0[4]; /* reserved */ ++ uint16_t oem_id; /* oem identifier */ ++ uint16_t oem_info; /* oem specific */ ++ uint16_t reserved1[10]; /* reserved */ ++ uint32_t peaddr; /* address of pe header */ ++ char message[64]; /* message to print */ ++}; ++ ++struct mz_reloc { ++ uint16_t offset; ++ uint16_t segment; ++}; ++ ++#define PE_MAGIC 0x00004550 /* "PE\0\0" */ ++#define PE_OPT_MAGIC_PE32 0x010b ++#define PE_OPT_MAGIC_PE32_ROM 0x0107 ++#define PE_OPT_MAGIC_PE32PLUS 0x020b ++ ++/* machine type */ ++#define IMAGE_FILE_MACHINE_UNKNOWN 0x0000 ++#define IMAGE_FILE_MACHINE_AM33 0x01d3 ++#define IMAGE_FILE_MACHINE_AMD64 0x8664 ++#define IMAGE_FILE_MACHINE_ARM 0x01c0 ++#define IMAGE_FILE_MACHINE_ARMV7 0x01c4 ++#define IMAGE_FILE_MACHINE_EBC 0x0ebc ++#define IMAGE_FILE_MACHINE_I386 0x014c ++#define IMAGE_FILE_MACHINE_IA64 0x0200 ++#define IMAGE_FILE_MACHINE_M32R 0x9041 ++#define IMAGE_FILE_MACHINE_MIPS16 0x0266 ++#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 ++#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 ++#define IMAGE_FILE_MACHINE_POWERPC 0x01f0 ++#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1 ++#define IMAGE_FILE_MACHINE_R4000 0x0166 ++#define IMAGE_FILE_MACHINE_SH3 0x01a2 ++#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3 ++#define IMAGE_FILE_MACHINE_SH3E 0x01a4 ++#define IMAGE_FILE_MACHINE_SH4 0x01a6 ++#define IMAGE_FILE_MACHINE_SH5 0x01a8 ++#define IMAGE_FILE_MACHINE_THUMB 0x01c2 ++#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 ++ ++/* flags */ ++#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 ++#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 ++#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 ++#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 ++#define IMAGE_FILE_AGGRESSIVE_WS_TRIM 0x0010 ++#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 ++#define IMAGE_FILE_16BIT_MACHINE 0x0040 ++#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 ++#define IMAGE_FILE_32BIT_MACHINE 0x0100 ++#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 ++#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 ++#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 ++#define IMAGE_FILE_SYSTEM 0x1000 ++#define IMAGE_FILE_DLL 0x2000 ++#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 ++#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 ++ ++struct pe_hdr { ++ uint32_t magic; /* PE magic */ ++ uint16_t machine; /* machine type */ ++ uint16_t sections; /* number of sections */ ++ uint32_t timestamp; /* time_t */ ++ uint32_t symbol_table; /* symbol table offset */ ++ uint32_t symbols; /* number of symbols */ ++ uint16_t opt_hdr_size; /* size of optional header */ ++ uint16_t flags; /* flags */ ++}; ++ ++#define IMAGE_FILE_OPT_ROM_MAGIC 0x107 ++#define IMAGE_FILE_OPT_PE32_MAGIC 0x10b ++#define IMAGE_FILE_OPT_PE32_PLUS_MAGIC 0x20b ++ ++#define IMAGE_SUBSYSTEM_UNKNOWN 0 ++#define IMAGE_SUBSYSTEM_NATIVE 1 ++#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 ++#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 ++#define IMAGE_SUBSYSTEM_POSIX_CUI 7 ++#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 ++#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 ++#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 ++#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 ++#define IMAGE_SUBSYSTEM_EFI_ROM_IMAGE 13 ++#define IMAGE_SUBSYSTEM_XBOX 14 ++ ++#define IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE 0x0040 ++#define IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY 0x0080 ++#define IMAGE_DLL_CHARACTERISTICS_NX_COMPAT 0x0100 ++#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200 ++#define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400 ++#define IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800 ++#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 ++#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000 ++ ++/* the fact that pe32 isn't padded where pe32+ is 64-bit means union won't ++ * work right. vomit. */ ++struct pe32_opt_hdr { ++ /* "standard" header */ ++ uint16_t magic; /* file type */ ++ uint8_t ld_major; /* linker major version */ ++ uint8_t ld_minor; /* linker minor version */ ++ uint32_t text_size; /* size of text section(s) */ ++ uint32_t data_size; /* size of data section(s) */ ++ uint32_t bss_size; /* size of bss section(s) */ ++ uint32_t entry_point; /* file offset of entry point */ ++ uint32_t code_base; /* relative code addr in ram */ ++ uint32_t data_base; /* relative data addr in ram */ ++ /* "windows" header */ ++ uint32_t image_base; /* preferred load address */ ++ uint32_t section_align; /* alignment in bytes */ ++ uint32_t file_align; /* file alignment in bytes */ ++ uint16_t os_major; /* major OS version */ ++ uint16_t os_minor; /* minor OS version */ ++ uint16_t image_major; /* major image version */ ++ uint16_t image_minor; /* minor image version */ ++ uint16_t subsys_major; /* major subsystem version */ ++ uint16_t subsys_minor; /* minor subsystem version */ ++ uint32_t win32_version; /* reserved, must be 0 */ ++ uint32_t image_size; /* image size */ ++ uint32_t header_size; /* header size rounded up to ++ file_align */ ++ uint32_t csum; /* checksum */ ++ uint16_t subsys; /* subsystem */ ++ uint16_t dll_flags; /* more flags! */ ++ uint32_t stack_size_req;/* amt of stack requested */ ++ uint32_t stack_size; /* amt of stack required */ ++ uint32_t heap_size_req; /* amt of heap requested */ ++ uint32_t heap_size; /* amt of heap required */ ++ uint32_t loader_flags; /* reserved, must be 0 */ ++ uint32_t data_dirs; /* number of data dir entries */ ++}; ++ ++struct pe32plus_opt_hdr { ++ uint16_t magic; /* file type */ ++ uint8_t ld_major; /* linker major version */ ++ uint8_t ld_minor; /* linker minor version */ ++ uint32_t text_size; /* size of text section(s) */ ++ uint32_t data_size; /* size of data section(s) */ ++ uint32_t bss_size; /* size of bss section(s) */ ++ uint32_t entry_point; /* file offset of entry point */ ++ uint32_t code_base; /* relative code addr in ram */ ++ /* "windows" header */ ++ uint64_t image_base; /* preferred load address */ ++ uint32_t section_align; /* alignment in bytes */ ++ uint32_t file_align; /* file alignment in bytes */ ++ uint16_t os_major; /* major OS version */ ++ uint16_t os_minor; /* minor OS version */ ++ uint16_t image_major; /* major image version */ ++ uint16_t image_minor; /* minor image version */ ++ uint16_t subsys_major; /* major subsystem version */ ++ uint16_t subsys_minor; /* minor subsystem version */ ++ uint32_t win32_version; /* reserved, must be 0 */ ++ uint32_t image_size; /* image size */ ++ uint32_t header_size; /* header size rounded up to ++ file_align */ ++ uint32_t csum; /* checksum */ ++ uint16_t subsys; /* subsystem */ ++ uint16_t dll_flags; /* more flags! */ ++ uint64_t stack_size_req;/* amt of stack requested */ ++ uint64_t stack_size; /* amt of stack required */ ++ uint64_t heap_size_req; /* amt of heap requested */ ++ uint64_t heap_size; /* amt of heap required */ ++ uint32_t loader_flags; /* reserved, must be 0 */ ++ uint32_t data_dirs; /* number of data dir entries */ ++}; ++ ++struct data_dirent { ++ uint32_t virtual_address; /* relative to load address */ ++ uint32_t size; ++}; ++ ++struct data_directory { ++ struct data_dirent exports; /* .edata */ ++ struct data_dirent imports; /* .idata */ ++ struct data_dirent resources; /* .rsrc */ ++ struct data_dirent exceptions; /* .pdata */ ++ struct data_dirent certs; /* certs */ ++ struct data_dirent base_relocations; /* .reloc */ ++ struct data_dirent debug; /* .debug */ ++ struct data_dirent arch; /* reservered */ ++ struct data_dirent global_ptr; /* global pointer reg. Size=0 */ ++ struct data_dirent tls; /* .tls */ ++ struct data_dirent load_config; /* load configuration structure */ ++ struct data_dirent bound_imports; /* no idea */ ++ struct data_dirent import_addrs; /* import address table */ ++ struct data_dirent delay_imports; /* delay-load import table */ ++ struct data_dirent clr_runtime_hdr; /* .cor (object only) */ ++ struct data_dirent reserved; ++}; ++ ++struct section_header { ++ char name[8]; /* name or "/12\0" string tbl offset */ ++ uint32_t virtual_size; /* size of loaded section in ram */ ++ uint32_t virtual_address; /* relative virtual address */ ++ uint32_t raw_data_size; /* size of the section */ ++ uint32_t data_addr; /* file pointer to first page of sec */ ++ uint32_t relocs; /* file pointer to relocation entries */ ++ uint32_t line_numbers; /* line numbers! */ ++ uint16_t num_relocs; /* number of relocations */ ++ uint16_t num_lin_numbers; /* srsly. */ ++ uint32_t flags; ++}; ++ ++/* they actually defined 0x00000000 as well, but I think we'll skip that one. */ ++#define IMAGE_SCN_RESERVED_0 0x00000001 ++#define IMAGE_SCN_RESERVED_1 0x00000002 ++#define IMAGE_SCN_RESERVED_2 0x00000004 ++#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 /* don't pad - obsolete */ ++#define IMAGE_SCN_RESERVED_3 0x00000010 ++#define IMAGE_SCN_CNT_CODE 0x00000020 /* .text */ ++#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 /* .data */ ++#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 /* .bss */ ++#define IMAGE_SCN_LNK_OTHER 0x00000100 /* reserved */ ++#define IMAGE_SCN_LNK_INFO 0x00000200 /* .drectve comments */ ++#define IMAGE_SCN_RESERVED_4 0x00000400 ++#define IMAGE_SCN_LNK_REMOVE 0x00000800 /* .o only - scn to be rm'd*/ ++#define IMAGE_SCN_LNK_COMDAT 0x00001000 /* .o only - COMDAT data */ ++#define IMAGE_SCN_RESERVED_5 0x00002000 /* spec omits this */ ++#define IMAGE_SCN_RESERVED_6 0x00004000 /* spec omits this */ ++#define IMAGE_SCN_GPREL 0x00008000 /* global pointer referenced data */ ++/* spec lists 0x20000 twice, I suspect they meant 0x10000 for one of them */ ++#define IMAGE_SCN_MEM_PURGEABLE 0x00010000 /* reserved for "future" use */ ++#define IMAGE_SCN_16BIT 0x00020000 /* reserved for "future" use */ ++#define IMAGE_SCN_LOCKED 0x00040000 /* reserved for "future" use */ ++#define IMAGE_SCN_PRELOAD 0x00080000 /* reserved for "future" use */ ++/* and here they just stuck a 1-byte integer in the middle of a bitfield */ ++#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 /* it does what it says on the box */ ++#define IMAGE_SCN_ALIGN_2BYTES 0x00200000 ++#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 ++#define IMAGE_SCN_ALIGN_8BYTES 0x00400000 ++#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 ++#define IMAGE_SCN_ALIGN_32BYTES 0x00600000 ++#define IMAGE_SCN_ALIGN_64BYTES 0x00700000 ++#define IMAGE_SCN_ALIGN_128BYTES 0x00800000 ++#define IMAGE_SCN_ALIGN_256BYTES 0x00900000 ++#define IMAGE_SCN_ALIGN_512BYTES 0x00a00000 ++#define IMAGE_SCN_ALIGN_1024BYTES 0x00b00000 ++#define IMAGE_SCN_ALIGN_2048BYTES 0x00c00000 ++#define IMAGE_SCN_ALIGN_4096BYTES 0x00d00000 ++#define IMAGE_SCN_ALIGN_8192BYTES 0x00e00000 ++#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 /* extended relocations */ ++#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 /* scn can be discarded */ ++#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 /* cannot be cached */ ++#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 /* not pageable */ ++#define IMAGE_SCN_MEM_SHARED 0x10000000 /* can be shared */ ++#define IMAGE_SCN_MEM_EXECUTE 0x20000000 /* can be executed as code */ ++#define IMAGE_SCN_MEM_READ 0x40000000 /* readable */ ++#define IMAGE_SCN_MEM_WRITE 0x80000000 /* writeable */ ++ ++enum x64_coff_reloc_type { ++ IMAGE_REL_AMD64_ABSOLUTE = 0, ++ IMAGE_REL_AMD64_ADDR64, ++ IMAGE_REL_AMD64_ADDR32, ++ IMAGE_REL_AMD64_ADDR32N, ++ IMAGE_REL_AMD64_REL32, ++ IMAGE_REL_AMD64_REL32_1, ++ IMAGE_REL_AMD64_REL32_2, ++ IMAGE_REL_AMD64_REL32_3, ++ IMAGE_REL_AMD64_REL32_4, ++ IMAGE_REL_AMD64_REL32_5, ++ IMAGE_REL_AMD64_SECTION, ++ IMAGE_REL_AMD64_SECREL, ++ IMAGE_REL_AMD64_SECREL7, ++ IMAGE_REL_AMD64_TOKEN, ++ IMAGE_REL_AMD64_SREL32, ++ IMAGE_REL_AMD64_PAIR, ++ IMAGE_REL_AMD64_SSPAN32, ++}; ++ ++enum arm_coff_reloc_type { ++ IMAGE_REL_ARM_ABSOLUTE, ++ IMAGE_REL_ARM_ADDR32, ++ IMAGE_REL_ARM_ADDR32N, ++ IMAGE_REL_ARM_BRANCH2, ++ IMAGE_REL_ARM_BRANCH1, ++ IMAGE_REL_ARM_SECTION, ++ IMAGE_REL_ARM_SECREL, ++}; ++ ++enum sh_coff_reloc_type { ++ IMAGE_REL_SH3_ABSOLUTE, ++ IMAGE_REL_SH3_DIRECT16, ++ IMAGE_REL_SH3_DIRECT32, ++ IMAGE_REL_SH3_DIRECT8, ++ IMAGE_REL_SH3_DIRECT8_WORD, ++ IMAGE_REL_SH3_DIRECT8_LONG, ++ IMAGE_REL_SH3_DIRECT4, ++ IMAGE_REL_SH3_DIRECT4_WORD, ++ IMAGE_REL_SH3_DIRECT4_LONG, ++ IMAGE_REL_SH3_PCREL8_WORD, ++ IMAGE_REL_SH3_PCREL8_LONG, ++ IMAGE_REL_SH3_PCREL12_WORD, ++ IMAGE_REL_SH3_STARTOF_SECTION, ++ IMAGE_REL_SH3_SIZEOF_SECTION, ++ IMAGE_REL_SH3_SECTION, ++ IMAGE_REL_SH3_SECREL, ++ IMAGE_REL_SH3_DIRECT32_NB, ++ IMAGE_REL_SH3_GPREL4_LONG, ++ IMAGE_REL_SH3_TOKEN, ++ IMAGE_REL_SHM_PCRELPT, ++ IMAGE_REL_SHM_REFLO, ++ IMAGE_REL_SHM_REFHALF, ++ IMAGE_REL_SHM_RELLO, ++ IMAGE_REL_SHM_RELHALF, ++ IMAGE_REL_SHM_PAIR, ++ IMAGE_REL_SHM_NOMODE, ++}; ++ ++enum ppc_coff_reloc_type { ++ IMAGE_REL_PPC_ABSOLUTE, ++ IMAGE_REL_PPC_ADDR64, ++ IMAGE_REL_PPC_ADDR32, ++ IMAGE_REL_PPC_ADDR24, ++ IMAGE_REL_PPC_ADDR16, ++ IMAGE_REL_PPC_ADDR14, ++ IMAGE_REL_PPC_REL24, ++ IMAGE_REL_PPC_REL14, ++ IMAGE_REL_PPC_ADDR32N, ++ IMAGE_REL_PPC_SECREL, ++ IMAGE_REL_PPC_SECTION, ++ IMAGE_REL_PPC_SECREL16, ++ IMAGE_REL_PPC_REFHI, ++ IMAGE_REL_PPC_REFLO, ++ IMAGE_REL_PPC_PAIR, ++ IMAGE_REL_PPC_SECRELLO, ++ IMAGE_REL_PPC_GPREL, ++ IMAGE_REL_PPC_TOKEN, ++}; ++ ++enum x86_coff_reloc_type { ++ IMAGE_REL_I386_ABSOLUTE, ++ IMAGE_REL_I386_DIR16, ++ IMAGE_REL_I386_REL16, ++ IMAGE_REL_I386_DIR32, ++ IMAGE_REL_I386_DIR32NB, ++ IMAGE_REL_I386_SEG12, ++ IMAGE_REL_I386_SECTION, ++ IMAGE_REL_I386_SECREL, ++ IMAGE_REL_I386_TOKEN, ++ IMAGE_REL_I386_SECREL7, ++ IMAGE_REL_I386_REL32, ++}; ++ ++enum ia64_coff_reloc_type { ++ IMAGE_REL_IA64_ABSOLUTE, ++ IMAGE_REL_IA64_IMM14, ++ IMAGE_REL_IA64_IMM22, ++ IMAGE_REL_IA64_IMM64, ++ IMAGE_REL_IA64_DIR32, ++ IMAGE_REL_IA64_DIR64, ++ IMAGE_REL_IA64_PCREL21B, ++ IMAGE_REL_IA64_PCREL21M, ++ IMAGE_REL_IA64_PCREL21F, ++ IMAGE_REL_IA64_GPREL22, ++ IMAGE_REL_IA64_LTOFF22, ++ IMAGE_REL_IA64_SECTION, ++ IMAGE_REL_IA64_SECREL22, ++ IMAGE_REL_IA64_SECREL64I, ++ IMAGE_REL_IA64_SECREL32, ++ IMAGE_REL_IA64_DIR32NB, ++ IMAGE_REL_IA64_SREL14, ++ IMAGE_REL_IA64_SREL22, ++ IMAGE_REL_IA64_SREL32, ++ IMAGE_REL_IA64_UREL32, ++ IMAGE_REL_IA64_PCREL60X, ++ IMAGE_REL_IA64_PCREL60B, ++ IMAGE_REL_IA64_PCREL60F, ++ IMAGE_REL_IA64_PCREL60I, ++ IMAGE_REL_IA64_PCREL60M, ++ IMAGE_REL_IA64_IMMGPREL6, ++ IMAGE_REL_IA64_TOKEN, ++ IMAGE_REL_IA64_GPREL32, ++ IMAGE_REL_IA64_ADDEND, ++}; ++ ++struct coff_reloc { ++ uint32_t virtual_address; ++ uint32_t symbol_table_index; ++ union { ++ enum x64_coff_reloc_type x64_type; ++ enum arm_coff_reloc_type arm_type; ++ enum sh_coff_reloc_type sh_type; ++ enum ppc_coff_reloc_type ppc_type; ++ enum x86_coff_reloc_type x86_type; ++ enum ia64_coff_reloc_type ia64_type; ++ uint16_t data; ++ }; ++}; ++ ++/* ++ * Definitions for the contents of the certs data block ++ */ ++#define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002 ++#define WIN_CERT_TYPE_EFI_OKCS115 0x0EF0 ++#define WIN_CERT_TYPE_EFI_GUID 0x0EF1 ++ ++#define WIN_CERT_REVISION_1_0 0x0100 ++#define WIN_CERT_REVISION_2_0 0x0200 ++ ++struct win_certificate { ++ uint32_t length; ++ uint16_t revision; ++ uint16_t cert_type; ++}; ++ ++#endif /* __LINUX_PE_H */ +-- +1.8.1.4 + + +From d329754b0c2881b6331aacafab74a26b2d9262b3 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:40 +0000 +Subject: [PATCH 21/47] pefile: Parse a PE binary to find a key and a signature + contained therein + +Parse a PE binary to find a key and a signature contained therein. Later +patches will check the signature and add the key if the signature checks out. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +--- + crypto/asymmetric_keys/Kconfig | 10 +- + crypto/asymmetric_keys/Makefile | 8 ++ + crypto/asymmetric_keys/pefile_parser.c | 185 +++++++++++++++++++++++++++++++++ + crypto/asymmetric_keys/pefile_parser.h | 31 ++++++ + 4 files changed, 233 insertions(+), 1 deletion(-) + create mode 100644 crypto/asymmetric_keys/pefile_parser.c + create mode 100644 crypto/asymmetric_keys/pefile_parser.h + +diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig +index 413f3f6..2e7315c 100644 +--- a/crypto/asymmetric_keys/Kconfig ++++ b/crypto/asymmetric_keys/Kconfig +@@ -31,7 +31,7 @@ config X509_CERTIFICATE_PARSER + select ASN1 + select OID_REGISTRY + help +- This option procides support for parsing X.509 format blobs for key ++ This option provides support for parsing X.509 format blobs for key + data and provides the ability to instantiate a crypto key from a + public key packet found inside the certificate. + +@@ -44,4 +44,12 @@ config PKCS7_MESSAGE_PARSER + This option provides support for parsing PKCS#7 format messages for + signature data and provides the ability to verify the signature. + ++config PE_FILE_PARSER ++ tristate "PE binary-wrapped key parser" ++ depends on X509_CERTIFICATE_PARSER ++ depends on PKCS7_MESSAGE_PARSER ++ help ++ This option provides support for parsing signed PE binaries that ++ contain an X.509 certificate in an internal section. ++ + endif # ASYMMETRIC_KEY_TYPE +diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile +index d63cb43..2675146 100644 +--- a/crypto/asymmetric_keys/Makefile ++++ b/crypto/asymmetric_keys/Makefile +@@ -40,3 +40,11 @@ $(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h + $(obj)/pkcs7-asn1.o: $(obj)/pkcs7-asn1.c $(obj)/pkcs7-asn1.h + + clean-files += pkcs7-asn1.c pkcs7-asn1.h ++ ++# ++# Signed PE binary-wrapped key handling ++# ++obj-$(CONFIG_PE_FILE_PARSER) += pefile_key_parser.o ++ ++pefile_key_parser-y := \ ++ pefile_parser.o +diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c +new file mode 100644 +index 0000000..fb80cf0 +--- /dev/null ++++ b/crypto/asymmetric_keys/pefile_parser.c +@@ -0,0 +1,185 @@ ++/* Parse a signed PE binary that wraps a key. ++ * ++ * Copyright (C) 2012 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) "PEFILE: "fmt ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "asymmetric_keys.h" ++#include "public_key.h" ++#include "pefile_parser.h" ++ ++/* ++ * Parse a PE binary. ++ */ ++static int pefile_parse_binary(struct key_preparsed_payload *prep, ++ struct pefile_context *ctx) ++{ ++ const struct mz_hdr *mz = prep->data; ++ const struct pe_hdr *pe; ++ const struct pe32_opt_hdr *pe32; ++ const struct pe32plus_opt_hdr *pe64; ++ const struct data_directory *ddir; ++ const struct data_dirent *dde; ++ const struct section_header *secs, *sec; ++ unsigned loop; ++ size_t cursor, datalen = prep->datalen; ++ ++ kenter(""); ++ ++#define chkaddr(base, x, s) \ ++ do { \ ++ if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \ ++ return -ELIBBAD; \ ++ } while(0) ++ ++ chkaddr(0, 0, sizeof(*mz)); ++ if (mz->magic != MZ_MAGIC) ++ return -ELIBBAD; ++ cursor = sizeof(*mz); ++ ++ chkaddr(cursor, mz->peaddr, sizeof(*pe)); ++ pe = prep->data + mz->peaddr; ++ if (pe->magic != PE_MAGIC) ++ return -ELIBBAD; ++ cursor = mz->peaddr + sizeof(*pe); ++ ++ chkaddr(0, cursor, sizeof(pe32->magic)); ++ pe32 = prep->data + cursor; ++ pe64 = prep->data + cursor; ++ ++ switch (pe32->magic) { ++ case PE_OPT_MAGIC_PE32: ++ chkaddr(0, cursor, sizeof(*pe32)); ++ ctx->image_checksum_offset = ++ (unsigned long)&pe32->csum - (unsigned long)prep->data; ++ ctx->header_size = pe32->header_size; ++ cursor += sizeof(*pe32); ++ ctx->n_data_dirents = pe32->data_dirs; ++ break; ++ ++ case PE_OPT_MAGIC_PE32PLUS: ++ chkaddr(0, cursor, sizeof(*pe64)); ++ ctx->image_checksum_offset = ++ (unsigned long)&pe64->csum - (unsigned long)prep->data; ++ ctx->header_size = pe64->header_size; ++ cursor += sizeof(*pe64); ++ ctx->n_data_dirents = pe64->data_dirs; ++ break; ++ ++ default: ++ pr_devel("Unknown PEOPT magic = %04hx\n", pe32->magic); ++ return -ELIBBAD; ++ } ++ ++ pr_devel("checksum @ %x\n", ctx->image_checksum_offset); ++ pr_devel("header size = %x\n", ctx->header_size); ++ ++ if (cursor >= ctx->header_size || ctx->header_size >= datalen) ++ return -ELIBBAD; ++ ++ if (ctx->n_data_dirents > (ctx->header_size - cursor) / sizeof(*dde) || ++ ctx->n_data_dirents < sizeof(*ddir) / sizeof(*dde)) ++ return -ELIBBAD; ++ ++ ddir = prep->data + cursor; ++ cursor += sizeof(*dde) * ctx->n_data_dirents; ++ ++ ctx->cert_dirent_offset = ++ (unsigned long)&ddir->certs - (unsigned long)prep->data; ++ ctx->certs_size = ddir->certs.size; ++ ++ if (!ddir->certs.virtual_address || !ddir->certs.size) { ++ pr_devel("Unsigned PE binary\n"); ++ return -EKEYREJECTED; ++ } ++ ++ chkaddr(ctx->header_size, ddir->certs.virtual_address, ddir->certs.size); ++ ctx->sig_offset = ddir->certs.virtual_address; ++ ctx->sig_len = ddir->certs.size; ++ pr_devel("cert = %x @%x [%*ph]\n", ++ ctx->sig_len, ctx->sig_offset, ++ ctx->sig_len, prep->data + ctx->sig_offset); ++ ++ /* Parse the section table, checking the parameters and looking for the ++ * section containing the list of keys. ++ */ ++ ctx->n_sections = pe->sections; ++ if (ctx->n_sections > (ctx->header_size - cursor) / sizeof(*sec)) ++ return -ELIBBAD; ++ ctx->secs = secs = prep->data + cursor; ++ cursor += sizeof(*sec) * ctx->n_sections; ++ ++ for (loop = 0; loop < ctx->n_sections; loop++) { ++ sec = &secs[loop]; ++ chkaddr(cursor, sec->data_addr, sec->raw_data_size); ++ if (memcmp(sec->name, ".keylist", 8) == 0) { ++ ctx->keylist_offset = sec->data_addr; ++ ctx->keylist_len = sec->raw_data_size; ++ } ++ } ++ ++ if (ctx->keylist_offset == 0) { ++ pr_devel("No .keylist section in PE binary\n"); ++ return -ENOENT; ++ } ++ ++ pr_devel("keylist = %x @%x [%*ph]\n", ++ ctx->keylist_len, ctx->keylist_offset, ++ ctx->keylist_len, prep->data + ctx->keylist_offset); ++ ++ return 0; ++} ++ ++/* ++ * Parse a PE binary. ++ */ ++static int pefile_key_preparse(struct key_preparsed_payload *prep) ++{ ++ struct pefile_context ctx; ++ int ret; ++ ++ kenter(""); ++ ++ memset(&ctx, 0, sizeof(ctx)); ++ ret = pefile_parse_binary(prep, &ctx); ++ if (ret < 0) ++ return ret; ++ ++ return -ENOANO; // Not yet complete ++} ++ ++static struct asymmetric_key_parser pefile_key_parser = { ++ .owner = THIS_MODULE, ++ .name = "pefile", ++ .parse = pefile_key_preparse, ++}; ++ ++/* ++ * Module stuff ++ */ ++static int __init pefile_key_init(void) ++{ ++ return register_asymmetric_key_parser(&pefile_key_parser); ++} ++ ++static void __exit pefile_key_exit(void) ++{ ++ unregister_asymmetric_key_parser(&pefile_key_parser); ++} ++ ++module_init(pefile_key_init); ++module_exit(pefile_key_exit); +diff --git a/crypto/asymmetric_keys/pefile_parser.h b/crypto/asymmetric_keys/pefile_parser.h +new file mode 100644 +index 0000000..82bcaf6 +--- /dev/null ++++ b/crypto/asymmetric_keys/pefile_parser.h +@@ -0,0 +1,31 @@ ++/* PE Binary parser bits ++ * ++ * Copyright (C) 2012 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. ++ */ ++#include "pkcs7_parser.h" ++ ++struct pefile_context { ++ unsigned header_size; ++ unsigned image_checksum_offset; ++ unsigned cert_dirent_offset; ++ unsigned n_data_dirents; ++ unsigned n_sections; ++ unsigned certs_size; ++ unsigned sig_offset; ++ unsigned sig_len; ++ unsigned keylist_offset; ++ unsigned keylist_len; ++ const struct section_header *secs; ++ struct pkcs7_message *pkcs7; ++ ++ /* PKCS#7 MS Individual Code Signing content */ ++ const void *digest; /* Digest */ ++ unsigned digest_len; /* Digest length */ ++ enum pkey_hash_algo digest_algo; /* Digest algorithm */ ++}; +-- +1.8.1.4 + + +From 3794d7963e17fc0b0c2f62164306b9a45cb2254e Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:40 +0000 +Subject: [PATCH 22/47] pefile: Strip the wrapper off of the cert data block + +The certificate data block in a PE binary has a wrapper around the PKCS#7 +signature we actually want to get at. Strip this off and check that we've got +something that appears to be a PKCS#7 signature. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +--- + crypto/asymmetric_keys/pefile_parser.c | 60 ++++++++++++++++++++++++++++++++++ + 1 file changed, 60 insertions(+) + +diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c +index fb80cf0..f2d4df0 100644 +--- a/crypto/asymmetric_keys/pefile_parser.c ++++ b/crypto/asymmetric_keys/pefile_parser.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -145,6 +146,61 @@ static int pefile_parse_binary(struct key_preparsed_payload *prep, + } + + /* ++ * Check and strip the PE wrapper from around the signature and check that the ++ * remnant looks something like PKCS#7. ++ */ ++static int pefile_strip_sig_wrapper(struct key_preparsed_payload *prep, ++ struct pefile_context *ctx) ++{ ++ struct win_certificate wrapper; ++ const u8 *pkcs7; ++ ++ if (ctx->sig_len < sizeof(wrapper)) { ++ pr_devel("Signature wrapper too short\n"); ++ return -ELIBBAD; ++ } ++ ++ memcpy(&wrapper, prep->data + ctx->sig_offset, sizeof(wrapper)); ++ pr_devel("sig wrapper = { %x, %x, %x }\n", ++ wrapper.length, wrapper.revision, wrapper.cert_type); ++ if (wrapper.length != ctx->sig_len) { ++ pr_devel("Signature wrapper len wrong\n"); ++ return -ELIBBAD; ++ } ++ if (wrapper.revision != WIN_CERT_REVISION_2_0) { ++ pr_devel("Signature is not revision 2.0\n"); ++ return -ENOTSUPP; ++ } ++ if (wrapper.cert_type != WIN_CERT_TYPE_PKCS_SIGNED_DATA) { ++ pr_devel("Signature certificate type is not PKCS\n"); ++ return -ENOTSUPP; ++ } ++ ++ ctx->sig_offset += sizeof(wrapper); ++ ctx->sig_len -= sizeof(wrapper); ++ if (ctx->sig_len == 0) { ++ pr_devel("Signature data missing\n"); ++ return -EKEYREJECTED; ++ } ++ ++ /* What's left should a PKCS#7 cert */ ++ pkcs7 = prep->data + ctx->sig_offset; ++ if (pkcs7[0] == (ASN1_CONS_BIT | ASN1_SEQ)) { ++ if (pkcs7[1] == 0x82 && ++ pkcs7[2] == (((ctx->sig_len - 4) >> 8) & 0xff) && ++ pkcs7[3] == ((ctx->sig_len - 4) & 0xff)) ++ return 0; ++ if (pkcs7[1] == 0x80) ++ return 0; ++ if (pkcs7[1] > 0x82) ++ return -EMSGSIZE; ++ } ++ ++ pr_devel("Signature data not PKCS#7\n"); ++ return -ELIBBAD; ++} ++ ++/* + * Parse a PE binary. + */ + static int pefile_key_preparse(struct key_preparsed_payload *prep) +@@ -159,6 +215,10 @@ static int pefile_key_preparse(struct key_preparsed_payload *prep) + if (ret < 0) + return ret; + ++ ret = pefile_strip_sig_wrapper(prep, &ctx); ++ if (ret < 0) ++ return ret; ++ + return -ENOANO; // Not yet complete + } + +-- +1.8.1.4 + + +From f23895761a15e08959140091dc17004e7e6e2035 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:40 +0000 +Subject: [PATCH 23/47] pefile: Parse the presumed PKCS#7 content of the + certificate blob + +Parse the content of the certificate blob, presuming it to be PKCS#7 format. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +--- + crypto/asymmetric_keys/pefile_parser.c | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c +index f2d4df0..056500f 100644 +--- a/crypto/asymmetric_keys/pefile_parser.c ++++ b/crypto/asymmetric_keys/pefile_parser.c +@@ -205,6 +205,7 @@ static int pefile_strip_sig_wrapper(struct key_preparsed_payload *prep, + */ + static int pefile_key_preparse(struct key_preparsed_payload *prep) + { ++ struct pkcs7_message *pkcs7; + struct pefile_context ctx; + int ret; + +@@ -219,7 +220,22 @@ static int pefile_key_preparse(struct key_preparsed_payload *prep) + if (ret < 0) + return ret; + +- return -ENOANO; // Not yet complete ++ pkcs7 = pkcs7_parse_message(prep->data + ctx.sig_offset, ctx.sig_len); ++ if (IS_ERR(pkcs7)) ++ return PTR_ERR(pkcs7); ++ ctx.pkcs7 = pkcs7; ++ ++ if (!ctx.pkcs7->data || !ctx.pkcs7->data_len) { ++ pr_devel("PKCS#7 message does not contain data\n"); ++ ret = -EBADMSG; ++ goto error; ++ } ++ ++ ret = -ENOANO; // Not yet complete ++ ++error: ++ pkcs7_free_message(ctx.pkcs7); ++ return ret; + } + + static struct asymmetric_key_parser pefile_key_parser = { +-- +1.8.1.4 + + +From fcdb91196beb6235eed676c368a662cbdf92b804 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:41 +0000 +Subject: [PATCH 24/47] pefile: Parse the "Microsoft individual code signing" + data blob + +The PKCS#7 certificate should contain a "Microsoft individual code signing" +data blob as its signed content. This blob contains a digest of the signed +content of the PE binary and the OID of the digest algorithm used (typically +SHA256). + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +--- + crypto/asymmetric_keys/Makefile | 9 ++- + crypto/asymmetric_keys/mscode.asn1 | 28 +++++++++ + crypto/asymmetric_keys/mscode_parser.c | 110 +++++++++++++++++++++++++++++++++ + crypto/asymmetric_keys/pefile_parser.c | 6 ++ + crypto/asymmetric_keys/pefile_parser.h | 5 ++ + include/linux/oid_registry.h | 6 +- + 6 files changed, 162 insertions(+), 2 deletions(-) + create mode 100644 crypto/asymmetric_keys/mscode.asn1 + create mode 100644 crypto/asymmetric_keys/mscode_parser.c + +diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile +index 2675146..ddc64bb 100644 +--- a/crypto/asymmetric_keys/Makefile ++++ b/crypto/asymmetric_keys/Makefile +@@ -47,4 +47,11 @@ clean-files += pkcs7-asn1.c pkcs7-asn1.h + obj-$(CONFIG_PE_FILE_PARSER) += pefile_key_parser.o + + pefile_key_parser-y := \ +- pefile_parser.o ++ pefile_parser.o \ ++ mscode_parser.o \ ++ mscode-asn1.o ++ ++$(obj)/mscode_parser.o: $(obj)/mscode-asn1.h $(obj)/mscode-asn1.h ++$(obj)/mscode-asn1.o: $(obj)/mscode-asn1.c $(obj)/mscode-asn1.h ++ ++clean-files += mscode-asn1.c mscode-asn1.h +diff --git a/crypto/asymmetric_keys/mscode.asn1 b/crypto/asymmetric_keys/mscode.asn1 +new file mode 100644 +index 0000000..6d09ba4 +--- /dev/null ++++ b/crypto/asymmetric_keys/mscode.asn1 +@@ -0,0 +1,28 @@ ++--- Microsoft individual code signing data blob parser ++--- ++--- Copyright (C) 2012 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. ++--- ++ ++MSCode ::= SEQUENCE { ++ type SEQUENCE { ++ contentType ContentType, ++ parameters ANY ++ }, ++ content SEQUENCE { ++ digestAlgorithm DigestAlgorithmIdentifier, ++ digest OCTET STRING ({ mscode_note_digest }) ++ } ++} ++ ++ContentType ::= OBJECT IDENTIFIER ({ mscode_note_content_type }) ++ ++DigestAlgorithmIdentifier ::= SEQUENCE { ++ algorithm OBJECT IDENTIFIER ({ mscode_note_digest_algo }), ++ parameters ANY OPTIONAL ++} +diff --git a/crypto/asymmetric_keys/mscode_parser.c b/crypto/asymmetric_keys/mscode_parser.c +new file mode 100644 +index 0000000..0bd68e0 +--- /dev/null ++++ b/crypto/asymmetric_keys/mscode_parser.c +@@ -0,0 +1,110 @@ ++/* Parse a Microsoft Individual Code Signing blob ++ * ++ * Copyright (C) 2012 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) "MSCODE: "fmt ++#include ++#include ++#include ++#include ++#include "pefile_parser.h" ++#include "mscode-asn1.h" ++ ++/* ++ * Parse a Microsoft Individual Code Signing blob ++ */ ++int mscode_parse(struct pefile_context *ctx) ++{ ++ pr_devel("Data: %zu [%*ph]\n", ++ ctx->pkcs7->data_len + ctx->pkcs7->data_hdrlen, ++ (unsigned)(ctx->pkcs7->data_len + ctx->pkcs7->data_hdrlen), ++ ctx->pkcs7->data - ctx->pkcs7->data_hdrlen); ++ ++ return asn1_ber_decoder(&mscode_decoder, ctx, ++ ctx->pkcs7->data - ctx->pkcs7->data_hdrlen, ++ ctx->pkcs7->data_len + ctx->pkcs7->data_hdrlen); ++} ++ ++/* ++ * Check the content type OID ++ */ ++int mscode_note_content_type(void *context, size_t hdrlen, ++ unsigned char tag, ++ const void *value, size_t vlen) ++{ ++ enum OID oid; ++ ++ oid = look_up_OID(value, vlen); ++ if (oid == OID__NR) { ++ char buffer[50]; ++ sprint_oid(value, vlen, buffer, sizeof(buffer)); ++ printk("MSCODE: Unknown OID: %s\n", buffer); ++ return -EBADMSG; ++ } ++ ++ if (oid != OID_msIndividualSPKeyPurpose) { ++ printk("MSCODE: Unexpected content type OID %u\n", oid); ++ return -EBADMSG; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Note the digest algorithm OID ++ */ ++int mscode_note_digest_algo(void *context, size_t hdrlen, ++ unsigned char tag, ++ const void *value, size_t vlen) ++{ ++ struct pefile_context *ctx = context; ++ char buffer[50]; ++ enum OID oid; ++ ++ oid = look_up_OID(value, vlen); ++ switch (oid) { ++ case OID_md4: ++ ctx->digest_algo = PKEY_HASH_MD4; ++ break; ++ case OID_md5: ++ ctx->digest_algo = PKEY_HASH_MD5; ++ break; ++ case OID_sha1: ++ ctx->digest_algo = PKEY_HASH_SHA1; ++ break; ++ case OID_sha256: ++ ctx->digest_algo = PKEY_HASH_SHA256; ++ break; ++ ++ case OID__NR: ++ sprint_oid(value, vlen, buffer, sizeof(buffer)); ++ printk("MSCODE: Unknown OID: %s\n", buffer); ++ return -EBADMSG; ++ ++ default: ++ printk("MSCODE: Unsupported content type: %u\n", oid); ++ return -ENOPKG; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Note the digest we're guaranteeing with this certificate ++ */ ++int mscode_note_digest(void *context, size_t hdrlen, ++ unsigned char tag, ++ const void *value, size_t vlen) ++{ ++ struct pefile_context *ctx = context; ++ ctx->digest = value; ++ ctx->digest_len = vlen; ++ return 0; ++} +diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c +index 056500f..f1c8cc1 100644 +--- a/crypto/asymmetric_keys/pefile_parser.c ++++ b/crypto/asymmetric_keys/pefile_parser.c +@@ -231,6 +231,12 @@ static int pefile_key_preparse(struct key_preparsed_payload *prep) + goto error; + } + ++ ret = mscode_parse(&ctx); ++ if (ret < 0) ++ goto error; ++ ++ pr_devel("Digest: %u [%*ph]\n", ctx.digest_len, ctx.digest_len, ctx.digest); ++ + ret = -ENOANO; // Not yet complete + + error: +diff --git a/crypto/asymmetric_keys/pefile_parser.h b/crypto/asymmetric_keys/pefile_parser.h +index 82bcaf6..c3462b7 100644 +--- a/crypto/asymmetric_keys/pefile_parser.h ++++ b/crypto/asymmetric_keys/pefile_parser.h +@@ -29,3 +29,8 @@ struct pefile_context { + unsigned digest_len; /* Digest length */ + enum pkey_hash_algo digest_algo; /* Digest algorithm */ + }; ++ ++/* ++ * mscode_parser.c ++ */ ++extern int mscode_parse(struct pefile_context *ctx); +diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h +index edeff85..332dcf5 100644 +--- a/include/linux/oid_registry.h ++++ b/include/linux/oid_registry.h +@@ -52,8 +52,12 @@ enum OID { + OID_md4, /* 1.2.840.113549.2.4 */ + OID_md5, /* 1.2.840.113549.2.5 */ + +- OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */ ++ /* Microsoft Authenticode & Software Publishing */ ++ OID_msIndirectData, /* 1.3.6.1.4.1.311.2.1.4 */ ++ OID_msIndividualSPKeyPurpose, /* 1.3.6.1.4.1.311.2.1.21 */ + OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */ ++ ++ OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */ + OID_sha1, /* 1.3.14.3.2.26 */ + OID_sha256, /* 2.16.840.1.101.3.4.2.1 */ + +-- +1.8.1.4 + + +From 63204898d9491f8ba1b90dea8660e8ff778db993 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:41 +0000 +Subject: [PATCH 25/47] pefile: Digest the PE binary and compare to the PKCS#7 + data + +Digest the signed parts of the PE binary, canonicalising the section table +before we need it, and then compare the the resulting digest to the one in the +PKCS#7 signed content. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +--- + crypto/asymmetric_keys/pefile_parser.c | 198 +++++++++++++++++++++++++++++++++ + 1 file changed, 198 insertions(+) + +diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c +index f1c8cc1..dfdb85e 100644 +--- a/crypto/asymmetric_keys/pefile_parser.c ++++ b/crypto/asymmetric_keys/pefile_parser.c +@@ -201,6 +201,193 @@ static int pefile_strip_sig_wrapper(struct key_preparsed_payload *prep, + } + + /* ++ * Compare two sections for canonicalisation. ++ */ ++static int pefile_compare_shdrs(const void *a, const void *b) ++{ ++ const struct section_header *shdra = a; ++ const struct section_header *shdrb = b; ++ int rc; ++ ++ if (shdra->data_addr > shdrb->data_addr) ++ return 1; ++ if (shdrb->data_addr > shdra->data_addr) ++ return -1; ++ ++ if (shdra->virtual_address > shdrb->virtual_address) ++ return 1; ++ if (shdrb->virtual_address > shdra->virtual_address) ++ return -1; ++ ++ rc = strcmp(shdra->name, shdrb->name); ++ if (rc != 0) ++ return rc; ++ ++ if (shdra->virtual_size > shdrb->virtual_size) ++ return 1; ++ if (shdrb->virtual_size > shdra->virtual_size) ++ return -1; ++ ++ if (shdra->raw_data_size > shdrb->raw_data_size) ++ return 1; ++ if (shdrb->raw_data_size > shdra->raw_data_size) ++ return -1; ++ ++ return 0; ++} ++ ++/* ++ * Load the contents of the PE binary into the digest, leaving out the image ++ * checksum and the certificate data block. ++ */ ++static int pefile_digest_pe_contents(struct key_preparsed_payload *prep, ++ struct pefile_context *ctx, ++ struct shash_desc *desc) ++{ ++ unsigned *canon, tmp, loop, i, hashed_bytes; ++ int ret; ++ ++ /* Digest the header and data directory, but leave out the image ++ * checksum and the data dirent for the signature. ++ */ ++ ret = crypto_shash_update(desc, prep->data, ctx->image_checksum_offset); ++ if (ret < 0) ++ return ret; ++ ++ tmp = ctx->image_checksum_offset + sizeof(uint32_t); ++ ret = crypto_shash_update(desc, prep->data + tmp, ++ ctx->cert_dirent_offset - tmp); ++ if (ret < 0) ++ return ret; ++ ++ tmp = ctx->cert_dirent_offset + sizeof(struct data_dirent); ++ ret = crypto_shash_update(desc, prep->data + tmp, ++ ctx->header_size - tmp); ++ if (ret < 0) ++ return ret; ++ ++ canon = kcalloc(ctx->n_sections, sizeof(unsigned), GFP_KERNEL); ++ if (!canon) ++ return -ENOMEM; ++ ++ /* We have to canonicalise the section table, so we perform an ++ * insertion sort. ++ */ ++ canon[0] = 0; ++ for (loop = 1; loop < ctx->n_sections; loop++) { ++ for (i = 0; i < loop; i++) { ++ if (pefile_compare_shdrs(&ctx->secs[canon[i]], ++ &ctx->secs[loop]) > 0) { ++ memmove(&canon[i + 1], &canon[i], ++ (loop - i) * sizeof(canon[0])); ++ break; ++ } ++ } ++ canon[i] = loop; ++ } ++ ++ hashed_bytes = ctx->header_size; ++ for (loop = 0; loop < ctx->n_sections; loop++) { ++ i = canon[loop]; ++ if (ctx->secs[i].raw_data_size == 0) ++ continue; ++ ret = crypto_shash_update(desc, ++ prep->data + ctx->secs[i].data_addr, ++ ctx->secs[i].raw_data_size); ++ if (ret < 0) { ++ kfree(canon); ++ return ret; ++ } ++ hashed_bytes += ctx->secs[i].raw_data_size; ++ } ++ kfree(canon); ++ ++ if (prep->datalen > hashed_bytes) { ++ tmp = hashed_bytes + ctx->certs_size; ++ ret = crypto_shash_update(desc, ++ prep->data + hashed_bytes, ++ prep->datalen - tmp); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Digest the contents of the PE binary, leaving out the image checksum and the ++ * certificate data block. ++ */ ++static int pefile_digest_pe(struct key_preparsed_payload *prep, ++ struct pefile_context *ctx) ++{ ++ struct crypto_shash *tfm; ++ struct shash_desc *desc; ++ size_t digest_size, desc_size; ++ void *digest; ++ int ret; ++ ++ kenter(",%u", ctx->digest_algo); ++ ++ /* Allocate the hashing algorithm we're going to need and find out how ++ * big the hash operational data will be. ++ */ ++ tfm = crypto_alloc_shash(pkey_hash_algo_name[ctx->digest_algo], 0, 0); ++ if (IS_ERR(tfm)) ++ return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); ++ ++ desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); ++ digest_size = crypto_shash_digestsize(tfm); ++ ++ if (digest_size != ctx->digest_len) { ++ pr_debug("Digest size mismatch (%zx != %x)\n", ++ digest_size, ctx->digest_len); ++ ret = -EBADMSG; ++ goto error_no_desc; ++ } ++ pr_devel("Digest: desc=%zu size=%zu\n", desc_size, digest_size); ++ ++ ret = -ENOMEM; ++ desc = kzalloc(desc_size + digest_size, GFP_KERNEL); ++ if (!desc) ++ goto error_no_desc; ++ ++ desc->tfm = tfm; ++ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; ++ ret = crypto_shash_init(desc); ++ if (ret < 0) ++ goto error; ++ ++ ret = pefile_digest_pe_contents(prep, ctx, desc); ++ if (ret < 0) ++ goto error; ++ ++ digest = (void *)desc + desc_size; ++ ret = crypto_shash_final(desc, digest); ++ if (ret < 0) ++ goto error; ++ ++ pr_devel("Digest calc = [%*ph]\n", ctx->digest_len, digest); ++ ++ /* Check that the PE file digest matches that in the MSCODE part of the ++ * PKCS#7 certificate. ++ */ ++ if (memcmp(digest, ctx->digest, ctx->digest_len) != 0) { ++ pr_debug("Digest mismatch\n"); ++ ret = -EKEYREJECTED; ++ } else { ++ pr_debug("The digests match!\n"); ++ } ++ ++error: ++ kfree(desc); ++error_no_desc: ++ crypto_free_shash(tfm); ++ kleave(" = %d", ret); ++ return ret; ++} ++ ++/* + * Parse a PE binary. + */ + static int pefile_key_preparse(struct key_preparsed_payload *prep) +@@ -237,6 +424,17 @@ static int pefile_key_preparse(struct key_preparsed_payload *prep) + + pr_devel("Digest: %u [%*ph]\n", ctx.digest_len, ctx.digest_len, ctx.digest); + ++ /* Generate the digest and check against the PKCS7 certificate ++ * contents. ++ */ ++ ret = pefile_digest_pe(prep, &ctx); ++ if (ret < 0) ++ goto error; ++ ++ ret = pkcs7_verify(pkcs7); ++ if (ret < 0) ++ goto error; ++ + ret = -ENOANO; // Not yet complete + + error: +-- +1.8.1.4 + + +From 17ed825e5f3f595665abd3fc11a6c180e6762b87 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Fri, 18 Jan 2013 13:58:35 +0000 +Subject: [PATCH 26/47] PEFILE: Validate PKCS#7 trust chain + +Validate the PKCS#7 trust chain against the contents of the system keyring. + +Signed-off-by: David Howells +--- + crypto/asymmetric_keys/Kconfig | 1 + + crypto/asymmetric_keys/pefile_parser.c | 5 +++++ + 2 files changed, 6 insertions(+) + +diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig +index 2e7315c..2777916 100644 +--- a/crypto/asymmetric_keys/Kconfig ++++ b/crypto/asymmetric_keys/Kconfig +@@ -48,6 +48,7 @@ config PE_FILE_PARSER + tristate "PE binary-wrapped key parser" + depends on X509_CERTIFICATE_PARSER + depends on PKCS7_MESSAGE_PARSER ++ depends on SYSTEM_TRUSTED_KEYRING + help + This option provides support for parsing signed PE binaries that + contain an X.509 certificate in an internal section. +diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c +index dfdb85e..edad948 100644 +--- a/crypto/asymmetric_keys/pefile_parser.c ++++ b/crypto/asymmetric_keys/pefile_parser.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + #include + #include "asymmetric_keys.h" + #include "public_key.h" +@@ -435,6 +436,10 @@ static int pefile_key_preparse(struct key_preparsed_payload *prep) + if (ret < 0) + goto error; + ++ ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &prep->trusted); ++ if (ret < 0) ++ goto error; ++ + ret = -ENOANO; // Not yet complete + + error: +-- +1.8.1.4 + + +From ce9ca4236f691264a94bcbe10beda9ec5a035baf Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Jan 2013 15:33:42 +0000 +Subject: [PATCH 27/47] PEFILE: Load the contained key if we consider the + container to be validly signed + +Load the key contained in the PE binary if the signature on the container can +be verified by following the chain of X.509 certificates in the PKCS#7 message +to a key that we already trust. Typically, the trusted key will be acquired +from a source outside of the kernel, such as the UEFI database. + +Signed-off-by: David Howells +Reviewed-by: Kees Cook +--- + crypto/asymmetric_keys/pefile_parser.c | 11 ++++++++++- + crypto/asymmetric_keys/x509_parser.h | 3 +++ + crypto/asymmetric_keys/x509_public_key.c | 3 ++- + 3 files changed, 15 insertions(+), 2 deletions(-) + +diff --git a/crypto/asymmetric_keys/pefile_parser.c b/crypto/asymmetric_keys/pefile_parser.c +index edad948..c3efe39 100644 +--- a/crypto/asymmetric_keys/pefile_parser.c ++++ b/crypto/asymmetric_keys/pefile_parser.c +@@ -395,6 +395,8 @@ static int pefile_key_preparse(struct key_preparsed_payload *prep) + { + struct pkcs7_message *pkcs7; + struct pefile_context ctx; ++ const void *saved_data; ++ size_t saved_datalen; + int ret; + + kenter(""); +@@ -440,7 +442,14 @@ static int pefile_key_preparse(struct key_preparsed_payload *prep) + if (ret < 0) + goto error; + +- ret = -ENOANO; // Not yet complete ++ /* We can now try to load the key */ ++ saved_data = prep->data; ++ saved_datalen = prep->datalen; ++ prep->data += ctx.keylist_offset; ++ prep->datalen = ctx.keylist_len; ++ ret = x509_key_preparse(prep); ++ prep->data = saved_data; ++ prep->datalen = saved_datalen; + + error: + pkcs7_free_message(ctx.pkcs7); +diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h +index 5e35fba..65452c4 100644 +--- a/crypto/asymmetric_keys/x509_parser.h ++++ b/crypto/asymmetric_keys/x509_parser.h +@@ -12,6 +12,8 @@ + #include + #include + ++struct key_preparsed_payload; ++ + struct x509_certificate { + struct x509_certificate *next; + const struct x509_certificate *signer; /* Certificate that signed this one */ +@@ -47,3 +49,4 @@ extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen + extern int x509_get_sig_params(struct x509_certificate *cert); + extern int x509_check_signature(const struct public_key *pub, + struct x509_certificate *cert); ++extern int x509_key_preparse(struct key_preparsed_payload *prep); +diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c +index 0f55e3b..c3e5a6d 100644 +--- a/crypto/asymmetric_keys/x509_public_key.c ++++ b/crypto/asymmetric_keys/x509_public_key.c +@@ -105,7 +105,7 @@ EXPORT_SYMBOL_GPL(x509_check_signature); + /* + * Attempt to parse a data blob for a key as an X509 certificate. + */ +-static int x509_key_preparse(struct key_preparsed_payload *prep) ++int x509_key_preparse(struct key_preparsed_payload *prep) + { + struct x509_certificate *cert; + struct tm now; +@@ -229,6 +229,7 @@ error_free_cert: + x509_free_certificate(cert); + return ret; + } ++EXPORT_SYMBOL_GPL(x509_key_preparse); + + static struct asymmetric_key_parser x509_key_parser = { + .owner = THIS_MODULE, +-- +1.8.1.4 + + +From 395cc1b55a0645ced39f92b31ba3bcc141e59383 Mon Sep 17 00:00:00 2001 +From: Chun-Yi Lee +Date: Thu, 21 Feb 2013 19:23:49 +0800 +Subject: [PATCH 28/47] MODSIGN: Fix including certificate twice when the + signing_key.x509 already exists + +This issue was found in devel-pekey branch on linux-modsign.git tree. The +x509_certificate_list includes certificate twice when the signing_key.x509 +already exists. +We can reproduce this issue by making kernel twice, the build log of +second time looks like this: + +... + CHK kernel/config_data.h + CERTS kernel/x509_certificate_list + - Including cert /ramdisk/working/joey/linux-modsign/signing_key.x509 + - Including cert signing_key.x509 +... + +Actually the build path was the same with the srctree path when building +kernel. It causes the size of bzImage increased by packaging certificates +twice. + +Cc: Rusty Russell +Cc: Josh Boyer +Cc: Randy Dunlap +Cc: Herbert Xu +Cc: "David S. Miller" +Cc: Michal Marek +Signed-off-by: Chun-Yi Lee +Signed-off-by: David Howells +--- + kernel/Makefile | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/kernel/Makefile b/kernel/Makefile +index ecff938..52f3426 100644 +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -149,7 +149,10 @@ $(obj)/timeconst.h: $(obj)/hz.bc $(src)/timeconst.bc FORCE + # + ############################################################################### + ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y) +-X509_CERTIFICATES-y := $(wildcard *.x509) $(wildcard $(srctree)/*.x509) ++X509_CERTIFICATES-y := $(wildcard *.x509) ++ifneq ($(shell pwd), $(srctree)) ++X509_CERTIFICATES-y += $(wildcard $(srctree)/*.x509) ++endif + X509_CERTIFICATES-$(CONFIG_MODULE_SIG) += signing_key.x509 + X509_CERTIFICATES := $(sort $(X509_CERTIFICATES-y)) + +-- +1.8.1.4 + + +From 0ef575739cff3fda47dd2a9415f066ab44dcc922 Mon Sep 17 00:00:00 2001 +From: Matthew Garrett +Date: Thu, 20 Sep 2012 10:40:56 -0400 +Subject: [PATCH 29/47] Secure boot: Add new capability + +Secure boot adds certain policy requirements, including that root must not +be able to do anything that could cause the kernel to execute arbitrary code. +The simplest way to handle this would seem to be to add a new capability +and gate various functionality on that. We'll then strip it from the initial +capability set if required. + +Signed-off-by: Matthew Garrett +--- + include/uapi/linux/capability.h | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/include/uapi/linux/capability.h b/include/uapi/linux/capability.h +index ba478fa..7109e65 100644 +--- a/include/uapi/linux/capability.h ++++ b/include/uapi/linux/capability.h +@@ -343,7 +343,11 @@ struct vfs_cap_data { + + #define CAP_BLOCK_SUSPEND 36 + +-#define CAP_LAST_CAP CAP_BLOCK_SUSPEND ++/* Allow things that trivially permit root to modify the running kernel */ ++ ++#define CAP_COMPROMISE_KERNEL 37 ++ ++#define CAP_LAST_CAP CAP_COMPROMISE_KERNEL + + #define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP) + +-- +1.8.1.4 + + +From 7312bed4fb9125d4880f11a64521b110079a3c0a Mon Sep 17 00:00:00 2001 +From: Josh Boyer +Date: Thu, 20 Sep 2012 10:41:05 -0400 +Subject: [PATCH 30/47] SELinux: define mapping for new Secure Boot capability + +Add the name of the new Secure Boot capability. This allows SELinux +policies to properly map CAP_COMPROMISE_KERNEL to the appropriate +capability class. + +Signed-off-by: Josh Boyer +--- + security/selinux/include/classmap.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h +index 14d04e6..ed99a2d 100644 +--- a/security/selinux/include/classmap.h ++++ b/security/selinux/include/classmap.h +@@ -146,8 +146,8 @@ struct security_class_mapping secclass_map[] = { + { "memprotect", { "mmap_zero", NULL } }, + { "peer", { "recv", NULL } }, + { "capability2", +- { "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend", +- NULL } }, ++ { "mac_override", "mac_admin", "syslog", "wake_alarm", ++ "block_suspend", "compromise_kernel", NULL } }, + { "kernel_service", { "use_as_override", "create_files_as", NULL } }, + { "tun_socket", + { COMMON_SOCK_PERMS, "attach_queue", NULL } }, +-- +1.8.1.4 + + +From e99e1273b0a50d874d2a53461e95f74460e1b812 Mon Sep 17 00:00:00 2001 +From: Josh Boyer +Date: Thu, 20 Sep 2012 10:41:02 -0400 +Subject: [PATCH 31/47] Secure boot: Add a dummy kernel parameter that will + switch on Secure Boot mode + +This forcibly drops CAP_COMPROMISE_KERNEL from both cap_permitted and cap_bset +in the init_cred struct, which everything else inherits from. This works on +any machine and can be used to develop even if the box doesn't have UEFI. + +Signed-off-by: Josh Boyer +--- + Documentation/kernel-parameters.txt | 7 +++++++ + kernel/cred.c | 17 +++++++++++++++++ + 2 files changed, 24 insertions(+) + +diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt +index 8c01a02..ee6c1ca 100644 +--- a/Documentation/kernel-parameters.txt ++++ b/Documentation/kernel-parameters.txt +@@ -2744,6 +2744,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted. + Note: increases power consumption, thus should only be + enabled if running jitter sensitive (HPC/RT) workloads. + ++ secureboot_enable= ++ [KNL] Enables an emulated UEFI Secure Boot mode. This ++ locks down various aspects of the kernel guarded by the ++ CAP_COMPROMISE_KERNEL capability. This includes things ++ like /dev/mem, IO port access, and other areas. It can ++ be used on non-UEFI machines for testing purposes. ++ + security= [SECURITY] Choose a security module to enable at boot. + If this boot parameter is not specified, only the first + security module asking for security registration will be +diff --git a/kernel/cred.c b/kernel/cred.c +index e0573a4..c3f4e3e 100644 +--- a/kernel/cred.c ++++ b/kernel/cred.c +@@ -565,6 +565,23 @@ void __init cred_init(void) + 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); + } + ++void __init secureboot_enable() ++{ ++ pr_info("Secure boot enabled\n"); ++ cap_lower((&init_cred)->cap_bset, CAP_COMPROMISE_KERNEL); ++ cap_lower((&init_cred)->cap_permitted, CAP_COMPROMISE_KERNEL); ++} ++ ++/* Dummy Secure Boot enable option to fake out UEFI SB=1 */ ++static int __init secureboot_enable_opt(char *str) ++{ ++ int sb_enable = !!simple_strtol(str, NULL, 0); ++ if (sb_enable) ++ secureboot_enable(); ++ return 1; ++} ++__setup("secureboot_enable=", secureboot_enable_opt); ++ + /** + * prepare_kernel_cred - Prepare a set of credentials for a kernel service + * @daemon: A userspace daemon to be used as a reference +-- +1.8.1.4 + + +From eeac2b5391d834eefebfae49a100244fdccc82e5 Mon Sep 17 00:00:00 2001 +From: Matthew Garrett +Date: Thu, 20 Sep 2012 10:41:03 -0400 +Subject: [PATCH 32/47] efi: Enable secure boot lockdown automatically when + enabled in firmware + +The firmware has a set of flags that indicate whether secure boot is enabled +and enforcing. Use them to indicate whether the kernel should lock itself +down. We also indicate the machine is in secure boot mode by adding the +EFI_SECURE_BOOT bit for use with efi_enabled. + +Signed-off-by: Matthew Garrett +Signed-off-by: Josh Boyer +--- + Documentation/x86/zero-page.txt | 2 ++ + arch/x86/boot/compressed/eboot.c | 32 ++++++++++++++++++++++++++++++++ + arch/x86/include/asm/bootparam_utils.h | 8 ++++++-- + arch/x86/include/uapi/asm/bootparam.h | 3 ++- + arch/x86/kernel/setup.c | 7 +++++++ + include/linux/cred.h | 2 ++ + include/linux/efi.h | 1 + + 7 files changed, 52 insertions(+), 3 deletions(-) + +diff --git a/Documentation/x86/zero-page.txt b/Documentation/x86/zero-page.txt +index 199f453..ff651d3 100644 +--- a/Documentation/x86/zero-page.txt ++++ b/Documentation/x86/zero-page.txt +@@ -30,6 +30,8 @@ Offset Proto Name Meaning + 1E9/001 ALL eddbuf_entries Number of entries in eddbuf (below) + 1EA/001 ALL edd_mbr_sig_buf_entries Number of entries in edd_mbr_sig_buffer + (below) ++1EB/001 ALL kbd_status Numlock is enabled ++1EC/001 ALL secure_boot Kernel should enable secure boot lockdowns + 1EF/001 ALL sentinel Used to detect broken bootloaders + 290/040 ALL edd_mbr_sig_buffer EDD MBR signatures + 2D0/A00 ALL e820_map E820 memory map table +diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c +index 35ee62f..0998ec7 100644 +--- a/arch/x86/boot/compressed/eboot.c ++++ b/arch/x86/boot/compressed/eboot.c +@@ -906,6 +906,36 @@ fail: + return status; + } + ++static int get_secure_boot(efi_system_table_t *_table) ++{ ++ u8 sb, setup; ++ unsigned long datasize = sizeof(sb); ++ efi_guid_t var_guid = EFI_GLOBAL_VARIABLE_GUID; ++ efi_status_t status; ++ ++ status = efi_call_phys5(sys_table->runtime->get_variable, ++ L"SecureBoot", &var_guid, NULL, &datasize, &sb); ++ ++ if (status != EFI_SUCCESS) ++ return 0; ++ ++ if (sb == 0) ++ return 0; ++ ++ ++ status = efi_call_phys5(sys_table->runtime->get_variable, ++ L"SetupMode", &var_guid, NULL, &datasize, ++ &setup); ++ ++ if (status != EFI_SUCCESS) ++ return 0; ++ ++ if (setup == 1) ++ return 0; ++ ++ return 1; ++} ++ + /* + * Because the x86 boot code expects to be passed a boot_params we + * need to create one ourselves (usually the bootloader would create +@@ -1200,6 +1230,8 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table, + if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) + goto fail; + ++ boot_params->secure_boot = get_secure_boot(sys_table); ++ + setup_graphics(boot_params); + + setup_efi_vars(boot_params); +diff --git a/arch/x86/include/asm/bootparam_utils.h b/arch/x86/include/asm/bootparam_utils.h +index 653668d..69a6c08 100644 +--- a/arch/x86/include/asm/bootparam_utils.h ++++ b/arch/x86/include/asm/bootparam_utils.h +@@ -38,9 +38,13 @@ static void sanitize_boot_params(struct boot_params *boot_params) + memset(&boot_params->olpc_ofw_header, 0, + (char *)&boot_params->efi_info - + (char *)&boot_params->olpc_ofw_header); +- memset(&boot_params->kbd_status, 0, ++ memset(&boot_params->kbd_status, 0, sizeof(boot_params->kbd_status)); ++ /* don't clear boot_params->secure_boot. we set that ourselves ++ * earlier. ++ */ ++ memset(&boot_params->_pad5[0], 0, + (char *)&boot_params->hdr - +- (char *)&boot_params->kbd_status); ++ (char *)&boot_params->_pad5[0]); + memset(&boot_params->_pad7[0], 0, + (char *)&boot_params->edd_mbr_sig_buffer[0] - + (char *)&boot_params->_pad7[0]); +diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h +index 0874424..56b7d39 100644 +--- a/arch/x86/include/uapi/asm/bootparam.h ++++ b/arch/x86/include/uapi/asm/bootparam.h +@@ -132,7 +132,8 @@ struct boot_params { + __u8 eddbuf_entries; /* 0x1e9 */ + __u8 edd_mbr_sig_buf_entries; /* 0x1ea */ + __u8 kbd_status; /* 0x1eb */ +- __u8 _pad5[3]; /* 0x1ec */ ++ __u8 secure_boot; /* 0x1ec */ ++ __u8 _pad5[2]; /* 0x1ed */ + /* + * The sentinel is set to a nonzero value (0xff) in header.S. + * +diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c +index 56f7fcf..3af6cf8 100644 +--- a/arch/x86/kernel/setup.c ++++ b/arch/x86/kernel/setup.c +@@ -1131,6 +1131,13 @@ void __init setup_arch(char **cmdline_p) + + io_delay_init(); + ++ if (boot_params.secure_boot) { ++#ifdef CONFIG_EFI ++ set_bit(EFI_SECURE_BOOT, &x86_efi_facility); ++#endif ++ secureboot_enable(); ++ } ++ + /* + * Parse the ACPI tables for possible boot-time SMP configuration. + */ +diff --git a/include/linux/cred.h b/include/linux/cred.h +index 04421e8..9e69542 100644 +--- a/include/linux/cred.h ++++ b/include/linux/cred.h +@@ -156,6 +156,8 @@ extern int set_security_override_from_ctx(struct cred *, const char *); + extern int set_create_files_as(struct cred *, struct inode *); + extern void __init cred_init(void); + ++extern void secureboot_enable(void); ++ + /* + * check for validity of credentials + */ +diff --git a/include/linux/efi.h b/include/linux/efi.h +index 2bc0ad7..10b167a 100644 +--- a/include/linux/efi.h ++++ b/include/linux/efi.h +@@ -634,6 +634,7 @@ extern int __init efi_setup_pcdp_console(char *); + #define EFI_RUNTIME_SERVICES 3 /* Can we use runtime services? */ + #define EFI_MEMMAP 4 /* Can we use EFI memory map? */ + #define EFI_64BIT 5 /* Is the firmware 64-bit? */ ++#define EFI_SECURE_BOOT 6 /* Are we in Secure Boot mode? */ + + #ifdef CONFIG_EFI + # ifdef CONFIG_X86 +-- +1.8.1.4 + + +From a1ac3b80b7a85d4fce665047b9701713fcfc1ea0 Mon Sep 17 00:00:00 2001 +From: Dave Howells +Date: Tue, 23 Oct 2012 09:30:54 -0400 +Subject: [PATCH 33/47] Add EFI signature data types + +Add the data types that are used for containing hashes, keys and certificates +for cryptographic verification. + +Signed-off-by: David Howells +--- + include/linux/efi.h | 20 ++++++++++++++++++++ + 1 file changed, 20 insertions(+) + +diff --git a/include/linux/efi.h b/include/linux/efi.h +index 10b167a..d3ef7c6 100644 +--- a/include/linux/efi.h ++++ b/include/linux/efi.h +@@ -389,6 +389,12 @@ typedef efi_status_t efi_query_variable_store_t(u32 attributes, unsigned long si + #define EFI_FILE_SYSTEM_GUID \ + EFI_GUID( 0x964e5b22, 0x6459, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b ) + ++#define EFI_CERT_SHA256_GUID \ ++ EFI_GUID( 0xc1c41626, 0x504c, 0x4092, 0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28 ) ++ ++#define EFI_CERT_X509_GUID \ ++ EFI_GUID( 0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72 ) ++ + typedef struct { + efi_guid_t guid; + u64 table; +@@ -524,6 +530,20 @@ typedef struct { + + #define EFI_INVALID_TABLE_ADDR (~0UL) + ++typedef struct { ++ efi_guid_t signature_owner; ++ u8 signature_data[]; ++} efi_signature_data_t; ++ ++typedef struct { ++ efi_guid_t signature_type; ++ u32 signature_list_size; ++ u32 signature_header_size; ++ u32 signature_size; ++ u8 signature_header[]; ++ /* efi_signature_data_t signatures[][] */ ++} efi_signature_list_t; ++ + /* + * All runtime access to EFI goes through this structure: + */ +-- +1.8.1.4 + + +From fac308c18ba449322666325f37f6a08ad818cf9f Mon Sep 17 00:00:00 2001 +From: Dave Howells +Date: Tue, 23 Oct 2012 09:36:28 -0400 +Subject: [PATCH 34/47] Add an EFI signature blob parser and key loader. + +X.509 certificates are loaded into the specified keyring as asymmetric type +keys. + +Signed-off-by: David Howells +--- + crypto/asymmetric_keys/Kconfig | 8 +++ + crypto/asymmetric_keys/Makefile | 1 + + crypto/asymmetric_keys/efi_parser.c | 109 ++++++++++++++++++++++++++++++++++++ + include/linux/efi.h | 4 ++ + 4 files changed, 122 insertions(+) + create mode 100644 crypto/asymmetric_keys/efi_parser.c + +diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig +index 2777916..429bbb7 100644 +--- a/crypto/asymmetric_keys/Kconfig ++++ b/crypto/asymmetric_keys/Kconfig +@@ -53,4 +53,12 @@ config PE_FILE_PARSER + This option provides support for parsing signed PE binaries that + contain an X.509 certificate in an internal section. + ++config EFI_SIGNATURE_LIST_PARSER ++ bool "EFI signature list parser" ++ depends on EFI ++ select X509_CERTIFICATE_PARSER ++ help ++ This option provides support for parsing EFI signature lists for ++ X.509 certificates and turning them into keys. ++ + endif # ASYMMETRIC_KEY_TYPE +diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile +index ddc64bb..360b308 100644 +--- a/crypto/asymmetric_keys/Makefile ++++ b/crypto/asymmetric_keys/Makefile +@@ -8,6 +8,7 @@ asymmetric_keys-y := asymmetric_type.o signature.o + + obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o + obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o ++obj-$(CONFIG_EFI_SIGNATURE_LIST_PARSER) += efi_parser.o + + # + # X.509 Certificate handling +diff --git a/crypto/asymmetric_keys/efi_parser.c b/crypto/asymmetric_keys/efi_parser.c +new file mode 100644 +index 0000000..424896a +--- /dev/null ++++ b/crypto/asymmetric_keys/efi_parser.c +@@ -0,0 +1,109 @@ ++/* EFI signature/key/certificate list parser ++ * ++ * Copyright (C) 2012 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) "EFI: "fmt ++#include ++#include ++#include ++#include ++#include ++ ++static __initdata efi_guid_t efi_cert_x509_guid = EFI_CERT_X509_GUID; ++ ++/** ++ * parse_efi_signature_list - Parse an EFI signature list for certificates ++ * @data: The data blob to parse ++ * @size: The size of the data blob ++ * @keyring: The keyring to add extracted keys to ++ */ ++int __init parse_efi_signature_list(const void *data, size_t size, struct key *keyring) ++{ ++ unsigned offs = 0; ++ size_t lsize, esize, hsize, elsize; ++ ++ pr_devel("-->%s(,%zu)\n", __func__, size); ++ ++ while (size > 0) { ++ efi_signature_list_t list; ++ const efi_signature_data_t *elem; ++ key_ref_t key; ++ ++ if (size < sizeof(list)) ++ return -EBADMSG; ++ ++ memcpy(&list, data, sizeof(list)); ++ pr_devel("LIST[%04x] guid=%pUl ls=%x hs=%x ss=%x\n", ++ offs, ++ list.signature_type.b, list.signature_list_size, ++ list.signature_header_size, list.signature_size); ++ ++ lsize = list.signature_list_size; ++ hsize = list.signature_header_size; ++ esize = list.signature_size; ++ elsize = lsize - sizeof(list) - hsize; ++ ++ if (lsize > size) { ++ pr_devel("<--%s() = -EBADMSG [overrun @%x]\n", ++ __func__, offs); ++ return -EBADMSG; ++ } ++ if (lsize < sizeof(list) || ++ lsize - sizeof(list) < hsize || ++ esize < sizeof(*elem) || ++ elsize < esize || ++ elsize % esize != 0) { ++ pr_devel("- bad size combo @%x\n", offs); ++ return -EBADMSG; ++ } ++ ++ if (efi_guidcmp(list.signature_type, efi_cert_x509_guid) != 0) { ++ data += lsize; ++ size -= lsize; ++ offs += lsize; ++ continue; ++ } ++ ++ data += sizeof(list) + hsize; ++ size -= sizeof(list) + hsize; ++ offs += sizeof(list) + hsize; ++ ++ for (; elsize > 0; elsize -= esize) { ++ elem = data; ++ ++ pr_devel("ELEM[%04x]\n", offs); ++ ++ key = key_create_or_update( ++ make_key_ref(keyring, 1), ++ "asymmetric", ++ NULL, ++ &elem->signature_data, ++ esize - sizeof(*elem), ++ (KEY_POS_ALL & ~KEY_POS_SETATTR) | ++ KEY_USR_VIEW, ++ KEY_ALLOC_NOT_IN_QUOTA | ++ KEY_ALLOC_TRUSTED); ++ ++ if (IS_ERR(key)) ++ pr_err("Problem loading in-kernel X.509 certificate (%ld)\n", ++ PTR_ERR(key)); ++ else ++ pr_notice("Loaded cert '%s' linked to '%s'\n", ++ key_ref_to_ptr(key)->description, ++ keyring->description); ++ ++ data += esize; ++ size -= esize; ++ offs += esize; ++ } ++ } ++ ++ return 0; ++} +diff --git a/include/linux/efi.h b/include/linux/efi.h +index d3ef7c6..4f0fbb7 100644 +--- a/include/linux/efi.h ++++ b/include/linux/efi.h +@@ -619,6 +619,10 @@ extern int efi_set_rtc_mmss(unsigned long nowtime); + extern void efi_reserve_boot_services(void); + extern struct efi_memory_map memmap; + ++struct key; ++extern int __init parse_efi_signature_list(const void *data, size_t size, ++ struct key *keyring); ++ + /** + * efi_range_is_wc - check the WC bit on an address range + * @start: starting kvirt address +-- +1.8.1.4 + + +From 75560e565cb8a4e853a3b6f6c65ed70c1ba29039 Mon Sep 17 00:00:00 2001 +From: Josh Boyer +Date: Fri, 26 Oct 2012 12:36:24 -0400 +Subject: [PATCH 35/47] KEYS: Add a system blacklist keyring + +This adds an additional keyring that is used to store certificates that +are blacklisted. This keyring is searched first when loading signed modules +and if the module's certificate is found, it will refuse to load. This is +useful in cases where third party certificates are used for module signing. + +Signed-off-by: Josh Boyer +--- + include/keys/system_keyring.h | 4 ++++ + init/Kconfig | 9 +++++++++ + kernel/module_signing.c | 12 ++++++++++++ + kernel/system_keyring.c | 17 +++++++++++++++++ + 4 files changed, 42 insertions(+) + +diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h +index 8dabc39..e466de1 100644 +--- a/include/keys/system_keyring.h ++++ b/include/keys/system_keyring.h +@@ -18,6 +18,10 @@ + + extern struct key *system_trusted_keyring; + ++#ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING ++extern struct key *system_blacklist_keyring; ++#endif ++ + #endif + + #endif /* _KEYS_SYSTEM_KEYRING_H */ +diff --git a/init/Kconfig b/init/Kconfig +index b9d8870..4f9771f 100644 +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1627,6 +1627,15 @@ config SYSTEM_TRUSTED_KEYRING + + Keys in this keyring are used by module signature checking. + ++config SYSTEM_BLACKLIST_KEYRING ++ bool "Provide system-wide ring of blacklisted keys" ++ depends on KEYS ++ help ++ Provide a system keyring to which blacklisted keys can be added. Keys ++ in the keyring are considered entirely untrusted. Keys in this keyring ++ are used by the module signature checking to reject loading of modules ++ signed with a blacklisted key. ++ + menuconfig MODULES + bool "Enable loadable module support" + help +diff --git a/kernel/module_signing.c b/kernel/module_signing.c +index 0b6b870..0a29b40 100644 +--- a/kernel/module_signing.c ++++ b/kernel/module_signing.c +@@ -158,6 +158,18 @@ static struct key *request_asymmetric_key(const char *signer, size_t signer_len, + + pr_debug("Look up: \"%s\"\n", id); + ++#ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING ++ key = keyring_search(make_key_ref(system_blacklist_keyring, 1), ++ &key_type_asymmetric, id); ++ if (!IS_ERR(key)) { ++ /* module is signed with a cert in the blacklist. reject */ ++ pr_err("Module key '%s' is in blacklist\n", id); ++ key_ref_put(key); ++ kfree(id); ++ return ERR_PTR(-EKEYREJECTED); ++ } ++#endif ++ + key = keyring_search(make_key_ref(system_trusted_keyring, 1), + &key_type_asymmetric, id); + if (IS_ERR(key)) +diff --git a/kernel/system_keyring.c b/kernel/system_keyring.c +index dae8778..2913c70 100644 +--- a/kernel/system_keyring.c ++++ b/kernel/system_keyring.c +@@ -20,6 +20,9 @@ + + struct key *system_trusted_keyring; + EXPORT_SYMBOL_GPL(system_trusted_keyring); ++#ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING ++struct key *system_blacklist_keyring; ++#endif + + extern __initdata const u8 system_certificate_list[]; + extern __initdata const u8 system_certificate_list_end[]; +@@ -41,6 +44,20 @@ static __init int system_trusted_keyring_init(void) + panic("Can't allocate system trusted keyring\n"); + + set_bit(KEY_FLAG_TRUSTED_ONLY, &system_trusted_keyring->flags); ++ ++#ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING ++ system_blacklist_keyring = keyring_alloc(".system_blacklist_keyring", ++ KUIDT_INIT(0), KGIDT_INIT(0), ++ current_cred(), ++ (KEY_POS_ALL & ~KEY_POS_SETATTR) | ++ KEY_USR_VIEW | KEY_USR_READ, ++ KEY_ALLOC_NOT_IN_QUOTA, NULL); ++ if (IS_ERR(system_blacklist_keyring)) ++ panic("Can't allocate system blacklist keyring\n"); ++ ++ set_bit(KEY_FLAG_TRUSTED_ONLY, &system_blacklist_keyring->flags); ++#endif ++ + return 0; + } + +-- +1.8.1.4 + + +From e46bf80471882ce1ab0b75dc954b2b59deec6fbb Mon Sep 17 00:00:00 2001 +From: Josh Boyer +Date: Fri, 26 Oct 2012 12:42:16 -0400 +Subject: [PATCH 36/47] MODSIGN: Import certificates from UEFI Secure Boot + +Secure Boot stores a list of allowed certificates in the 'db' variable. +This imports those certificates into the system trusted keyring. This +allows for a third party signing certificate to be used in conjunction +with signed modules. By importing the public certificate into the 'db' +variable, a user can allow a module signed with that certificate to +load. The shim UEFI bootloader has a similar certificate list stored +in the 'MokListRT' variable. We import those as well. + +In the opposite case, Secure Boot maintains a list of disallowed +certificates in the 'dbx' variable. We load those certificates into +the newly introduced system blacklist keyring and forbid any module +signed with those from loading. + +Signed-off-by: Josh Boyer +--- + include/linux/efi.h | 6 ++++ + init/Kconfig | 9 +++++ + kernel/Makefile | 3 ++ + kernel/modsign_uefi.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 110 insertions(+) + create mode 100644 kernel/modsign_uefi.c + +diff --git a/include/linux/efi.h b/include/linux/efi.h +index 4f0fbb7..7ac7a17 100644 +--- a/include/linux/efi.h ++++ b/include/linux/efi.h +@@ -395,6 +395,12 @@ typedef efi_status_t efi_query_variable_store_t(u32 attributes, unsigned long si + #define EFI_CERT_X509_GUID \ + EFI_GUID( 0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72 ) + ++#define EFI_IMAGE_SECURITY_DATABASE_GUID \ ++ EFI_GUID( 0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f ) ++ ++#define EFI_SHIM_LOCK_GUID \ ++ EFI_GUID( 0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 ) ++ + typedef struct { + efi_guid_t guid; + u64 table; +diff --git a/init/Kconfig b/init/Kconfig +index 4f9771f..da92f1c 100644 +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -1745,6 +1745,15 @@ config MODULE_SIG_ALL + comment "Do not forget to sign required modules with scripts/sign-file" + depends on MODULE_SIG_FORCE && !MODULE_SIG_ALL + ++config MODULE_SIG_UEFI ++ bool "Allow modules signed with certs stored in UEFI" ++ depends on MODULE_SIG && SYSTEM_BLACKLIST_KEYRING && EFI ++ select EFI_SIGNATURE_LIST_PARSER ++ help ++ This will import certificates stored in UEFI and allow modules ++ signed with those to be loaded. It will also disallow loading ++ of modules stored in the UEFI dbx variable. ++ + choice + prompt "Which hash algorithm should modules be signed with?" + depends on MODULE_SIG +diff --git a/kernel/Makefile b/kernel/Makefile +index 52f3426..e2a616f 100644 +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -55,6 +55,7 @@ obj-$(CONFIG_UID16) += uid16.o + obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o + obj-$(CONFIG_MODULES) += module.o + obj-$(CONFIG_MODULE_SIG) += module_signing.o ++obj-$(CONFIG_MODULE_SIG_UEFI) += modsign_uefi.o + obj-$(CONFIG_KALLSYMS) += kallsyms.o + obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o + obj-$(CONFIG_KEXEC) += kexec.o +@@ -114,6 +115,8 @@ obj-$(CONFIG_CONTEXT_TRACKING) += context_tracking.o + + $(obj)/configs.o: $(obj)/config_data.h + ++$(obj)/modsign_uefi.o: KBUILD_CFLAGS += -fshort-wchar ++ + # config_data.h contains the same information as ikconfig.h but gzipped. + # Info from config_data can be extracted from /proc/config* + targets += config_data.gz +diff --git a/kernel/modsign_uefi.c b/kernel/modsign_uefi.c +new file mode 100644 +index 0000000..94b0eb3 +--- /dev/null ++++ b/kernel/modsign_uefi.c +@@ -0,0 +1,92 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "module-internal.h" ++ ++static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, unsigned long *size) ++{ ++ efi_status_t status; ++ unsigned long lsize = 4; ++ unsigned long tmpdb[4]; ++ void *db = NULL; ++ ++ status = efi.get_variable(name, guid, NULL, &lsize, &tmpdb); ++ if (status != EFI_BUFFER_TOO_SMALL) { ++ pr_err("Couldn't get size: 0x%lx\n", status); ++ return NULL; ++ } ++ ++ db = kmalloc(lsize, GFP_KERNEL); ++ if (!db) { ++ pr_err("Couldn't allocate memory for uefi cert list\n"); ++ goto out; ++ } ++ ++ status = efi.get_variable(name, guid, NULL, &lsize, db); ++ if (status != EFI_SUCCESS) { ++ kfree(db); ++ db = NULL; ++ pr_err("Error reading db var: 0x%lx\n", status); ++ } ++out: ++ *size = lsize; ++ return db; ++} ++ ++/* ++ * * Load the certs contained in the UEFI databases ++ * */ ++static int __init load_uefi_certs(void) ++{ ++ efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; ++ efi_guid_t mok_var = EFI_SHIM_LOCK_GUID; ++ void *db = NULL, *dbx = NULL, *mok = NULL; ++ unsigned long dbsize = 0, dbxsize = 0, moksize = 0; ++ int rc = 0; ++ ++ /* Check if SB is enabled and just return if not */ ++ if (!efi_enabled(EFI_SECURE_BOOT)) ++ return 0; ++ ++ /* Get db, MokListRT, and dbx. They might not exist, so it isn't ++ * an error if we can't get them. ++ */ ++ db = get_cert_list(L"db", &secure_var, &dbsize); ++ if (!db) { ++ pr_err("MODSIGN: Couldn't get UEFI db list\n"); ++ } else { ++ rc = parse_efi_signature_list(db, dbsize, system_trusted_keyring); ++ if (rc) ++ pr_err("Couldn't parse db signatures: %d\n", rc); ++ kfree(db); ++ } ++ ++ mok = get_cert_list(L"MokListRT", &mok_var, &moksize); ++ if (!mok) { ++ pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); ++ } else { ++ rc = parse_efi_signature_list(mok, moksize, system_trusted_keyring); ++ if (rc) ++ pr_err("Couldn't parse MokListRT signatures: %d\n", rc); ++ kfree(mok); ++ } ++ ++ dbx = get_cert_list(L"dbx", &secure_var, &dbxsize); ++ if (!dbx) { ++ pr_info("MODSIGN: Couldn't get UEFI dbx list\n"); ++ } else { ++ rc = parse_efi_signature_list(dbx, dbxsize, ++ system_blacklist_keyring); ++ if (rc) ++ pr_err("Couldn't parse dbx signatures: %d\n", rc); ++ kfree(dbx); ++ } ++ ++ return rc; ++} ++late_initcall(load_uefi_certs); +-- +1.8.1.4 + + +From 8724600edad99706cce510645eff15f28787561a Mon Sep 17 00:00:00 2001 +From: Matthew Garrett +Date: Thu, 20 Sep 2012 10:40:57 -0400 +Subject: [PATCH 37/47] PCI: Lock down BAR access in secure boot environments + +Any hardware that can potentially generate DMA has to be locked down from +userspace in order to avoid it being possible for an attacker to cause +arbitrary kernel behaviour. Default to paranoid - in future we can +potentially relax this for sufficiently IOMMU-isolated devices. + +Signed-off-by: Matthew Garrett +--- + drivers/pci/pci-sysfs.c | 9 +++++++++ + drivers/pci/proc.c | 8 +++++++- + drivers/pci/syscall.c | 2 +- + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c +index 5b4a9d9..db2ff9e 100644 +--- a/drivers/pci/pci-sysfs.c ++++ b/drivers/pci/pci-sysfs.c +@@ -622,6 +622,9 @@ pci_write_config(struct file* filp, struct kobject *kobj, + loff_t init_off = off; + u8 *data = (u8*) buf; + ++ if (!capable(CAP_COMPROMISE_KERNEL)) ++ return -EPERM; ++ + if (off > dev->cfg_size) + return 0; + if (off + count > dev->cfg_size) { +@@ -928,6 +931,9 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, + resource_size_t start, end; + int i; + ++ if (!capable(CAP_COMPROMISE_KERNEL)) ++ return -EPERM; ++ + for (i = 0; i < PCI_ROM_RESOURCE; i++) + if (res == &pdev->resource[i]) + break; +@@ -1035,6 +1041,9 @@ pci_write_resource_io(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t off, size_t count) + { ++ if (!capable(CAP_COMPROMISE_KERNEL)) ++ return -EPERM; ++ + return pci_resource_io(filp, kobj, attr, buf, off, count, true); + } + +diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c +index 0812608..544132d 100644 +--- a/drivers/pci/proc.c ++++ b/drivers/pci/proc.c +@@ -136,6 +136,9 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof + int size = dev->cfg_size; + int cnt; + ++ if (!capable(CAP_COMPROMISE_KERNEL)) ++ return -EPERM; ++ + if (pos >= size) + return 0; + if (nbytes >= size) +@@ -215,6 +218,9 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd, + #endif /* HAVE_PCI_MMAP */ + int ret = 0; + ++ if (!capable(CAP_COMPROMISE_KERNEL)) ++ return -EPERM; ++ + switch (cmd) { + case PCIIOC_CONTROLLER: + ret = pci_domain_nr(dev->bus); +@@ -253,7 +259,7 @@ static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma) + struct pci_filp_private *fpriv = file->private_data; + int i, ret; + +- if (!capable(CAP_SYS_RAWIO)) ++ if (!capable(CAP_SYS_RAWIO) || !capable(CAP_COMPROMISE_KERNEL)) + return -EPERM; + + /* Make sure the caller is mapping a real resource for this device */ +diff --git a/drivers/pci/syscall.c b/drivers/pci/syscall.c +index e1c1ec5..97e785f 100644 +--- a/drivers/pci/syscall.c ++++ b/drivers/pci/syscall.c +@@ -92,7 +92,7 @@ SYSCALL_DEFINE5(pciconfig_write, unsigned long, bus, unsigned long, dfn, + u32 dword; + int err = 0; + +- if (!capable(CAP_SYS_ADMIN)) ++ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_COMPROMISE_KERNEL)) + return -EPERM; + + dev = pci_get_bus_and_slot(bus, dfn); +-- +1.8.1.4 + + +From 2361c561632c00e3974a092454ecc7daafb7cdf6 Mon Sep 17 00:00:00 2001 +From: Matthew Garrett +Date: Thu, 20 Sep 2012 10:40:58 -0400 +Subject: [PATCH 38/47] x86: Lock down IO port access in secure boot + environments + +IO port access would permit users to gain access to PCI configuration +registers, which in turn (on a lot of hardware) give access to MMIO register +space. This would potentially permit root to trigger arbitrary DMA, so lock +it down by default. + +Signed-off-by: Matthew Garrett +--- + arch/x86/kernel/ioport.c | 4 ++-- + drivers/char/mem.c | 3 +++ + 2 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/arch/x86/kernel/ioport.c b/arch/x86/kernel/ioport.c +index 4ddaf66..f505995 100644 +--- a/arch/x86/kernel/ioport.c ++++ b/arch/x86/kernel/ioport.c +@@ -28,7 +28,7 @@ asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int turn_on) + + if ((from + num <= from) || (from + num > IO_BITMAP_BITS)) + return -EINVAL; +- if (turn_on && !capable(CAP_SYS_RAWIO)) ++ if (turn_on && (!capable(CAP_SYS_RAWIO) || !capable(CAP_COMPROMISE_KERNEL))) + return -EPERM; + + /* +@@ -103,7 +103,7 @@ SYSCALL_DEFINE1(iopl, unsigned int, level) + return -EINVAL; + /* Trying to gain more privileges? */ + if (level > old) { +- if (!capable(CAP_SYS_RAWIO)) ++ if (!capable(CAP_SYS_RAWIO) || !capable(CAP_COMPROMISE_KERNEL)) + return -EPERM; + } + regs->flags = (regs->flags & ~X86_EFLAGS_IOPL) | (level << 12); +diff --git a/drivers/char/mem.c b/drivers/char/mem.c +index 2c644af..7eee4d8 100644 +--- a/drivers/char/mem.c ++++ b/drivers/char/mem.c +@@ -597,6 +597,9 @@ static ssize_t write_port(struct file *file, const char __user *buf, + unsigned long i = *ppos; + const char __user *tmp = buf; + ++ if (!capable(CAP_COMPROMISE_KERNEL)) ++ return -EPERM; ++ + if (!access_ok(VERIFY_READ, buf, count)) + return -EFAULT; + while (count-- > 0 && i < 65536) { +-- +1.8.1.4 + + +From e97f4dd5b1baaae0854e8a5c87aa4be4d03d1854 Mon Sep 17 00:00:00 2001 +From: Matthew Garrett +Date: Thu, 20 Sep 2012 10:40:59 -0400 +Subject: [PATCH 39/47] ACPI: Limit access to custom_method + +It must be impossible for even root to get code executed in kernel context +under a secure boot environment. custom_method effectively allows arbitrary +access to system memory, so it needs to have a capability check here. + +Signed-off-by: Matthew Garrett +--- + drivers/acpi/custom_method.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/acpi/custom_method.c b/drivers/acpi/custom_method.c +index 12b62f2..edf0710 100644 +--- a/drivers/acpi/custom_method.c ++++ b/drivers/acpi/custom_method.c +@@ -29,6 +29,9 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf, + struct acpi_table_header table; + acpi_status status; + ++ if (!capable(CAP_COMPROMISE_KERNEL)) ++ return -EPERM; ++ + if (!(*ppos)) { + /* parse the table header to get the table length */ + if (count <= sizeof(struct acpi_table_header)) +-- +1.8.1.4 + + +From f0389c3a6d823e2386ab4e21d9e012c4ebd310ac Mon Sep 17 00:00:00 2001 +From: Matthew Garrett +Date: Thu, 20 Sep 2012 10:41:00 -0400 +Subject: [PATCH 40/47] asus-wmi: Restrict debugfs interface + +We have no way of validating what all of the Asus WMI methods do on a +given machine, and there's a risk that some will allow hardware state to +be manipulated in such a way that arbitrary code can be executed in the +kernel. Add a capability check to prevent that. + +Signed-off-by: Matthew Garrett +--- + drivers/platform/x86/asus-wmi.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c +index c11b242..6d5f88f 100644 +--- a/drivers/platform/x86/asus-wmi.c ++++ b/drivers/platform/x86/asus-wmi.c +@@ -1617,6 +1617,9 @@ static int show_dsts(struct seq_file *m, void *data) + int err; + u32 retval = -1; + ++ if (!capable(CAP_COMPROMISE_KERNEL)) ++ return -EPERM; ++ + err = asus_wmi_get_devstate(asus, asus->debug.dev_id, &retval); + + if (err < 0) +@@ -1633,6 +1636,9 @@ static int show_devs(struct seq_file *m, void *data) + int err; + u32 retval = -1; + ++ if (!capable(CAP_COMPROMISE_KERNEL)) ++ return -EPERM; ++ + err = asus_wmi_set_devstate(asus->debug.dev_id, asus->debug.ctrl_param, + &retval); + +@@ -1657,6 +1663,9 @@ static int show_call(struct seq_file *m, void *data) + union acpi_object *obj; + acpi_status status; + ++ if (!capable(CAP_COMPROMISE_KERNEL)) ++ return -EPERM; ++ + status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, + 1, asus->debug.method_id, + &input, &output); +-- +1.8.1.4 + + +From 2e507337fc23547c7a15e5a102647becf20dba77 Mon Sep 17 00:00:00 2001 +From: Matthew Garrett +Date: Thu, 20 Sep 2012 10:41:01 -0400 +Subject: [PATCH 41/47] Restrict /dev/mem and /dev/kmem in secure boot setups + +Allowing users to write to address space makes it possible for the kernel +to be subverted. Restrict this when we need to protect the kernel. + +Signed-off-by: Matthew Garrett +--- + drivers/char/mem.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/char/mem.c b/drivers/char/mem.c +index 7eee4d8..772ee2b 100644 +--- a/drivers/char/mem.c ++++ b/drivers/char/mem.c +@@ -158,6 +158,9 @@ static ssize_t write_mem(struct file *file, const char __user *buf, + unsigned long copied; + void *ptr; + ++ if (!capable(CAP_COMPROMISE_KERNEL)) ++ return -EPERM; ++ + if (!valid_phys_addr_range(p, count)) + return -EFAULT; + +@@ -530,6 +533,9 @@ static ssize_t write_kmem(struct file *file, const char __user *buf, + char *kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */ + int err = 0; + ++ if (!capable(CAP_COMPROMISE_KERNEL)) ++ return -EPERM; ++ + if (p < (unsigned long) high_memory) { + unsigned long to_write = min_t(unsigned long, count, + (unsigned long)high_memory - p); +-- +1.8.1.4 + + +From ff22d9716846844f8c249dbc965684a8014efed0 Mon Sep 17 00:00:00 2001 +From: Josh Boyer +Date: Thu, 20 Sep 2012 10:41:04 -0400 +Subject: [PATCH 42/47] acpi: Ignore acpi_rsdp kernel parameter in a secure + boot environment + +This option allows userspace to pass the RSDP address to the kernel. This +could potentially be used to circumvent the secure boot trust model. +This is setup through the setup_arch function, which is called before the +security_init function sets up the security_ops, so we cannot use a +capable call here. We ignore the setting if we are booted in Secure Boot +mode. + +Signed-off-by: Josh Boyer +--- + drivers/acpi/osl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c +index e721863..ed82da7 100644 +--- a/drivers/acpi/osl.c ++++ b/drivers/acpi/osl.c +@@ -245,7 +245,7 @@ early_param("acpi_rsdp", setup_acpi_rsdp); + acpi_physical_address __init acpi_os_get_root_pointer(void) + { + #ifdef CONFIG_KEXEC +- if (acpi_rsdp) ++ if (acpi_rsdp && !efi_enabled(EFI_SECURE_BOOT)) + return acpi_rsdp; + #endif + +-- +1.8.1.4 + + +From b08ac626fbcf917bc219133d49c347d7d58eaae1 Mon Sep 17 00:00:00 2001 +From: Matthew Garrett +Date: Tue, 4 Sep 2012 11:55:13 -0400 +Subject: [PATCH 43/47] kexec: Disable in a secure boot environment + +kexec could be used as a vector for a malicious user to use a signed kernel +to circumvent the secure boot trust model. In the long run we'll want to +support signed kexec payloads, but for the moment we should just disable +loading entirely in that situation. + +Signed-off-by: Matthew Garrett +--- + kernel/kexec.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/kernel/kexec.c b/kernel/kexec.c +index 59f7b55..8bf1336 100644 +--- a/kernel/kexec.c ++++ b/kernel/kexec.c +@@ -939,7 +939,7 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments, + int result; + + /* We only trust the superuser with rebooting the system. */ +- if (!capable(CAP_SYS_BOOT)) ++ if (!capable(CAP_SYS_BOOT) || !capable(CAP_COMPROMISE_KERNEL)) + return -EPERM; + + /* +-- +1.8.1.4 + + +From f0d9c2906c1145585882fb7eb167e47e998c2e24 Mon Sep 17 00:00:00 2001 +From: Josh Boyer +Date: Fri, 5 Oct 2012 10:12:48 -0400 +Subject: [PATCH 44/47] MODSIGN: Always enforce module signing in a Secure Boot + environment + +If a machine is booted into a Secure Boot environment, we need to +protect the trust model. This requires that all modules be signed +with a key that is in the kernel's _modsign keyring. The checks for +this are already done via the 'sig_enforce' module parameter. Make +this visible within the kernel and force it to be true. + +Signed-off-by: Josh Boyer +--- + kernel/cred.c | 8 ++++++++ + kernel/module.c | 4 ++-- + 2 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/kernel/cred.c b/kernel/cred.c +index c3f4e3e..c5554e0 100644 +--- a/kernel/cred.c ++++ b/kernel/cred.c +@@ -565,11 +565,19 @@ void __init cred_init(void) + 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); + } + ++#ifdef CONFIG_MODULE_SIG ++extern bool sig_enforce; ++#endif ++ + void __init secureboot_enable() + { + pr_info("Secure boot enabled\n"); + cap_lower((&init_cred)->cap_bset, CAP_COMPROMISE_KERNEL); + cap_lower((&init_cred)->cap_permitted, CAP_COMPROMISE_KERNEL); ++#ifdef CONFIG_MODULE_SIG ++ /* Enable module signature enforcing */ ++ sig_enforce = true; ++#endif + } + + /* Dummy Secure Boot enable option to fake out UEFI SB=1 */ +diff --git a/kernel/module.c b/kernel/module.c +index 0925c9a..af4a476 100644 +--- a/kernel/module.c ++++ b/kernel/module.c +@@ -109,9 +109,9 @@ struct list_head *kdb_modules = &modules; /* kdb needs the list of modules */ + + #ifdef CONFIG_MODULE_SIG + #ifdef CONFIG_MODULE_SIG_FORCE +-static bool sig_enforce = true; ++bool sig_enforce = true; + #else +-static bool sig_enforce = false; ++bool sig_enforce = false; + + static int param_set_bool_enable_only(const char *val, + const struct kernel_param *kp) +-- +1.8.1.4 + + +From 1c6bfec7db39e46eeb456fb84e3153281690bbe0 Mon Sep 17 00:00:00 2001 +From: Josh Boyer +Date: Fri, 26 Oct 2012 14:02:09 -0400 +Subject: [PATCH 45/47] hibernate: Disable in a Secure Boot environment + +There is currently no way to verify the resume image when returning +from hibernate. This might compromise the secure boot trust model, +so until we can work with signed hibernate images we disable it in +a Secure Boot environment. + +Signed-off-by: Josh Boyer +--- + kernel/power/hibernate.c | 15 ++++++++++++++- + kernel/power/main.c | 7 ++++++- + kernel/power/user.c | 3 +++ + 3 files changed, 23 insertions(+), 2 deletions(-) + +diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c +index b26f5f1..7f63cb4 100644 +--- a/kernel/power/hibernate.c ++++ b/kernel/power/hibernate.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + + #include "power.h" + +@@ -632,6 +633,10 @@ int hibernate(void) + { + int error; + ++ if (!capable(CAP_COMPROMISE_KERNEL)) { ++ return -EPERM; ++ } ++ + lock_system_sleep(); + /* The snapshot device should not be opened while we're running */ + if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { +@@ -723,7 +728,7 @@ static int software_resume(void) + /* + * If the user said "noresume".. bail out early. + */ +- if (noresume) ++ if (noresume || !capable(CAP_COMPROMISE_KERNEL)) + return 0; + + /* +@@ -889,6 +894,11 @@ static ssize_t disk_show(struct kobject *kobj, struct kobj_attribute *attr, + int i; + char *start = buf; + ++ if (efi_enabled(EFI_SECURE_BOOT)) { ++ buf += sprintf(buf, "[%s]\n", "disabled"); ++ return buf-start; ++ } ++ + for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) { + if (!hibernation_modes[i]) + continue; +@@ -923,6 +933,9 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr, + char *p; + int mode = HIBERNATION_INVALID; + ++ if (!capable(CAP_COMPROMISE_KERNEL)) ++ return -EPERM; ++ + p = memchr(buf, '\n', n); + len = p ? p - buf : n; + +diff --git a/kernel/power/main.c b/kernel/power/main.c +index d77663b..78f8ed5 100644 +--- a/kernel/power/main.c ++++ b/kernel/power/main.c +@@ -15,6 +15,7 @@ + #include + #include + #include ++#include + + #include "power.h" + +@@ -301,7 +302,11 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, + } + #endif + #ifdef CONFIG_HIBERNATION +- s += sprintf(s, "%s\n", "disk"); ++ if (!efi_enabled(EFI_SECURE_BOOT)) { ++ s += sprintf(s, "%s\n", "disk"); ++ } else { ++ s += sprintf(s, "\n"); ++ } + #else + if (s != buf) + /* convert the last space to a newline */ +diff --git a/kernel/power/user.c b/kernel/power/user.c +index 4ed81e7..b11a0f4 100644 +--- a/kernel/power/user.c ++++ b/kernel/power/user.c +@@ -48,6 +48,9 @@ static int snapshot_open(struct inode *inode, struct file *filp) + struct snapshot_data *data; + int error; + ++ if (!capable(CAP_COMPROMISE_KERNEL)) ++ return -EPERM; ++ + lock_system_sleep(); + + if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { +-- +1.8.1.4 + + +From 07cda990d2f18774522889ece30bddf67c703157 Mon Sep 17 00:00:00 2001 +From: Josh Boyer +Date: Tue, 5 Feb 2013 19:25:05 -0500 +Subject: [PATCH 46/47] efi: Disable secure boot if shim is in insecure mode + +A user can manually tell the shim boot loader to disable validation of +images it loads. When a user does this, it creates a UEFI variable called +MokSBState that does not have the runtime attribute set. Given that the +user explicitly disabled validation, we can honor that and not enable +secure boot mode if that variable is set. + +Signed-off-by: Josh Boyer +--- + arch/x86/boot/compressed/eboot.c | 20 +++++++++++++++++++- + 1 file changed, 19 insertions(+), 1 deletion(-) + +diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c +index 0998ec7..4945ee5 100644 +--- a/arch/x86/boot/compressed/eboot.c ++++ b/arch/x86/boot/compressed/eboot.c +@@ -908,8 +908,9 @@ fail: + + static int get_secure_boot(efi_system_table_t *_table) + { +- u8 sb, setup; ++ u8 sb, setup, moksbstate; + unsigned long datasize = sizeof(sb); ++ u32 attr; + efi_guid_t var_guid = EFI_GLOBAL_VARIABLE_GUID; + efi_status_t status; + +@@ -933,6 +934,23 @@ static int get_secure_boot(efi_system_table_t *_table) + if (setup == 1) + return 0; + ++ /* See if a user has put shim into insecure_mode. If so, and the variable ++ * doesn't have the runtime attribute set, we might as well honor that. ++ */ ++ var_guid = EFI_SHIM_LOCK_GUID; ++ status = efi_call_phys5(sys_table->runtime->get_variable, ++ L"MokSBState", &var_guid, &attr, &datasize, ++ &moksbstate); ++ ++ /* If it fails, we don't care why. Default to secure */ ++ if (status != EFI_SUCCESS) ++ return 1; ++ ++ if (!(attr & EFI_VARIABLE_RUNTIME_ACCESS)) { ++ if (moksbstate == 1) ++ return 0; ++ } ++ + return 1; + } + +-- +1.8.1.4 + + +From e61066577405c37c2758f9b7fb2694967bdbe921 Mon Sep 17 00:00:00 2001 +From: Kees Cook +Date: Fri, 8 Feb 2013 11:12:13 -0800 +Subject: [PATCH 47/47] x86: Lock down MSR writing in secure boot + +Writing to MSRs should not be allowed unless CAP_COMPROMISE_KERNEL is +set since it could lead to execution of arbitrary code in kernel mode. + +Signed-off-by: Kees Cook +--- + arch/x86/kernel/msr.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/arch/x86/kernel/msr.c b/arch/x86/kernel/msr.c +index ce13049..fa4dc6c 100644 +--- a/arch/x86/kernel/msr.c ++++ b/arch/x86/kernel/msr.c +@@ -103,6 +103,9 @@ static ssize_t msr_write(struct file *file, const char __user *buf, + int err = 0; + ssize_t bytes = 0; + ++ if (!capable(CAP_COMPROMISE_KERNEL)) ++ return -EPERM; ++ + if (count % 8) + return -EINVAL; /* Invalid chunk size */ + +@@ -150,6 +153,10 @@ static long msr_ioctl(struct file *file, unsigned int ioc, unsigned long arg) + err = -EBADF; + break; + } ++ if (!capable(CAP_COMPROMISE_KERNEL)) { ++ err = -EPERM; ++ break; ++ } + if (copy_from_user(®s, uregs, sizeof regs)) { + err = -EFAULT; + break; +-- +1.8.1.4 + diff --git a/drivers-hwmon-nct6775.patch b/drivers-hwmon-nct6775.patch deleted file mode 100644 index f6ee15c8c..000000000 --- a/drivers-hwmon-nct6775.patch +++ /dev/null @@ -1,6424 +0,0 @@ -Add support for nct6775.c - -This driver is needed on modern Asus motherboards like P8Z77-M PRO, and -other motherboards based on Z77 chipset. - -This patch folds the changes found on those 3.10-rc6 changesets: - -169c05c hwmon: (nct6775) Do not create non-existing attributes -6445e66 hwmon: (nct6775) Fix coding style problems -6d4b362 hwmon: (nct6775) Constify strings -c409fd4 hwmon: (nct6775) Use ARRAY_SIZE for loops where possible -573728c hwmon: (nct6775) Enable both AUXTIN and VIN3 on NCT6776 -2c7fd30 hwmon: (nct6775) Expand scope of supported chips -236d903 hwmon: (nct6775) Drop read/write lock -0fc1f8f hwmon: (nct6775) Only report VID if supported and enabled -8e9285b hwmon: (nct6775) Detect and report additional temperature sources -bbd8dec hwmon: (nct6775) Add support for weighted fan control -cdcaece hwmon: (nct6775) Add support for automatic fan control -77eb5b3 hwmon: (nct6775) Add support for pwm, pwm_mode, and pwm_enable -84d19d9 hwmon: (nct6775) Add power management support -47ece96 hwmon: (nct6775) Add support for fan debounce module parameter -5c25d95 hwmon: (nct6775) Add support for fanX_pulses sysfs attribute -1c65dc3 hwmon: (nct6775) Add support for fan speed attributes -aa136e5 hwmon: (nct6775) Add support for temperature sensors -a6bd587 hwmon: (nct6775) Add case open detection -9de2e2e hwmon: Driver for Nuvoton NCT6775F, NCT6776F, and NCT6779D - -- - -From 9de2e2e84e7d52e4c2a9e1a1e21ab6ac686233c0 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Sun, 20 May 2012 19:29:48 -0700 -Subject: [PATCH] hwmon: Driver for Nuvoton NCT6775F, NCT6776F, and NCT6779D - -This driver will replace the w83627ehf driver for NCT6775F and NCT6776F, -and provides support for NCT6779D. - -This patch provides support for voltage monitor attributes. - -Signed-off-by: Guenter Roeck - -diff --git a/Documentation/hwmon/nct6775 b/Documentation/hwmon/nct6775 -new file mode 100644 -index 0000000..ccfd5cc ---- /dev/null -+++ b/Documentation/hwmon/nct6775 -@@ -0,0 +1,81 @@ -+Note -+==== -+ -+This driver supersedes the NCT6775F and NCT6776F support in the W83627EHF -+driver. -+ -+Kernel driver NCT6775 -+===================== -+ -+Supported chips: -+ * Nuvoton NCT6775F/W83667HG-I -+ Prefix: 'nct6775' -+ Addresses scanned: ISA address retrieved from Super I/O registers -+ Datasheet: Available from Nuvoton upon request -+ * Nuvoton NCT6776F -+ Prefix: 'nct6776' -+ Addresses scanned: ISA address retrieved from Super I/O registers -+ Datasheet: Available from Nuvoton upon request -+ * Nuvoton NCT6779D -+ Prefix: 'nct6779' -+ Addresses scanned: ISA address retrieved from Super I/O registers -+ Datasheet: Available from Nuvoton upon request -+ -+Authors: -+ Guenter Roeck -+ -+Description -+----------- -+ -+This driver implements support for the Nuvoton NCT6775F, NCT6776F, and NCT6779D -+super I/O chips. -+ -+The chips support up to 25 temperature monitoring sources. Up to 6 of those are -+direct temperature sensor inputs, the others are special sources such as PECI, -+PCH, and SMBUS. Depending on the chip type, 2 to 6 of the temperature sources -+can be monitored and compared against minimum, maximum, and critical -+temperatures. The driver reports up to 10 of the temperatures to the user. -+There are 4 to 5 fan rotation speed sensors, 8 to 15 analog voltage sensors, -+one VID, alarms with beep warnings (control unimplemented), and some automatic -+fan regulation strategies (plus manual fan control mode). -+ -+The temperature sensor sources on all chips are configurable. The configured -+source for each of the temperature sensors is provided in tempX_label. -+ -+Temperatures are measured in degrees Celsius and measurement resolution is -+either 1 degC or 0.5 degC, depending on the temperature source and -+configuration. An alarm is triggered when the temperature gets higher than -+the high limit; it stays on until the temperature falls below the hysteresis -+value. Alarms are only supported for temp1 to temp6, depending on the chip type. -+ -+Fan rotation speeds are reported in RPM (rotations per minute). An alarm is -+triggered if the rotation speed has dropped below a programmable limit. On -+NCT6775F, fan readings can be divided by a programmable divider (1, 2, 4, 8, -+16, 32, 64 or 128) to give the readings more range or accuracy; the other chips -+do not have a fan speed divider. The driver sets the most suitable fan divisor -+itself; specifically, it doubles the divider value each time a fan speed reading -+returns an invalid value. Some fans might not be present because they share pins -+with other functions. -+ -+Voltage sensors (also known as IN sensors) report their values in millivolts. -+An alarm is triggered if the voltage has crossed a programmable minimum -+or maximum limit. -+ -+The driver supports automatic fan control mode known as Thermal Cruise. -+In this mode, the chip attempts to keep the measured temperature in a -+predefined temperature range. If the temperature goes out of range, fan -+is driven slower/faster to reach the predefined range again. -+ -+The mode works for fan1-fan5. -+ -+Usage Notes -+----------- -+ -+On various ASUS boards with NCT6776F, it appears that CPUTIN is not really -+connected to anything and floats, or that it is connected to some non-standard -+temperature measurement device. As a result, the temperature reported on CPUTIN -+will not reflect a usable value. It often reports unreasonably high -+temperatures, and in some cases the reported temperature declines if the actual -+temperature increases (similar to the raw PECI temperature value - see PECI -+specification for details). CPUTIN should therefore be be ignored on ASUS -+boards. The CPU temperature on ASUS boards is reported from PECI 0. -diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig -index 47d2176..a0f1d6a 100644 ---- a/drivers/hwmon/Kconfig -+++ b/drivers/hwmon/Kconfig -@@ -897,6 +897,19 @@ config SENSORS_MCP3021 - This driver can also be built as a module. If so, the module - will be called mcp3021. - -+config SENSORS_NCT6775 -+ tristate "Nuvoton NCT6775F, NCT6776F, NCT6779D" -+ depends on !PPC -+ select HWMON_VID -+ help -+ If you say yes here you get support for the hardware monitoring -+ functionality of the Nuvoton NCT6775F, NCT6776F, and NCT6779D -+ Super-I/O chips. This driver replaces the w83627ehf driver for -+ NCT6775F and NCT6776F. -+ -+ This driver can also be built as a module. If so, the module -+ will be called nct6775. -+ - config SENSORS_NTC_THERMISTOR - tristate "NTC thermistor support" - depends on (!OF && !IIO) || (OF && IIO) -diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile -index 5d36a57..8297572 100644 ---- a/drivers/hwmon/Makefile -+++ b/drivers/hwmon/Makefile -@@ -105,6 +105,7 @@ obj-$(CONFIG_SENSORS_MAX6650) += max6650.o - obj-$(CONFIG_SENSORS_MAX6697) += max6697.o - obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o - obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o -+obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o - obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o - obj-$(CONFIG_SENSORS_PC87360) += pc87360.o - obj-$(CONFIG_SENSORS_PC87427) += pc87427.o -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -new file mode 100644 -index 0000000..f75cd82 ---- /dev/null -+++ b/drivers/hwmon/nct6775.c -@@ -0,0 +1,1021 @@ -+/* -+ * nct6775 - Driver for the hardware monitoring functionality of -+ * Nuvoton NCT677x Super-I/O chips -+ * -+ * Copyright (C) 2012 Guenter Roeck -+ * -+ * Derived from w83627ehf driver -+ * Copyright (C) 2005-2012 Jean Delvare -+ * Copyright (C) 2006 Yuan Mu (Winbond), -+ * Rudolf Marek -+ * David Hubbard -+ * Daniel J Blueman -+ * Copyright (C) 2010 Sheng-Yuan Huang (Nuvoton) (PS00) -+ * -+ * Shamelessly ripped from the w83627hf driver -+ * Copyright (C) 2003 Mark Studebaker -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -+ * -+ * -+ * Supports the following chips: -+ * -+ * Chip #vin #fan #pwm #temp chip IDs man ID -+ * nct6775f 9 4 3 6+3 0xb470 0xc1 0x5ca3 -+ * nct6776f 9 5 3 6+3 0xc330 0xc1 0x5ca3 -+ * nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3 -+ * -+ * #temp lists the number of monitored temperature sources (first value) plus -+ * the number of directly connectable temperature sensors (second value). -+ */ -+ -+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "lm75.h" -+ -+enum kinds { nct6775, nct6776, nct6779 }; -+ -+/* used to set data->name = nct6775_device_names[data->sio_kind] */ -+static const char * const nct6775_device_names[] = { -+ "nct6775", -+ "nct6776", -+ "nct6779", -+}; -+ -+static unsigned short force_id; -+module_param(force_id, ushort, 0); -+MODULE_PARM_DESC(force_id, "Override the detected device ID"); -+ -+#define DRVNAME "nct6775" -+ -+/* -+ * Super-I/O constants and functions -+ */ -+ -+#define NCT6775_LD_HWM 0x0b -+#define NCT6775_LD_VID 0x0d -+ -+#define SIO_REG_LDSEL 0x07 /* Logical device select */ -+#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ -+#define SIO_REG_ENABLE 0x30 /* Logical device enable */ -+#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ -+ -+#define SIO_NCT6775_ID 0xb470 -+#define SIO_NCT6776_ID 0xc330 -+#define SIO_NCT6779_ID 0xc560 -+#define SIO_ID_MASK 0xFFF0 -+ -+static inline void -+superio_outb(int ioreg, int reg, int val) -+{ -+ outb(reg, ioreg); -+ outb(val, ioreg + 1); -+} -+ -+static inline int -+superio_inb(int ioreg, int reg) -+{ -+ outb(reg, ioreg); -+ return inb(ioreg + 1); -+} -+ -+static inline void -+superio_select(int ioreg, int ld) -+{ -+ outb(SIO_REG_LDSEL, ioreg); -+ outb(ld, ioreg + 1); -+} -+ -+static inline int -+superio_enter(int ioreg) -+{ -+ /* -+ * Try to reserve and for exclusive access. -+ */ -+ if (!request_muxed_region(ioreg, 2, DRVNAME)) -+ return -EBUSY; -+ -+ outb(0x87, ioreg); -+ outb(0x87, ioreg); -+ -+ return 0; -+} -+ -+static inline void -+superio_exit(int ioreg) -+{ -+ outb(0xaa, ioreg); -+ outb(0x02, ioreg); -+ outb(0x02, ioreg + 1); -+ release_region(ioreg, 2); -+} -+ -+/* -+ * ISA constants -+ */ -+ -+#define IOREGION_ALIGNMENT (~7) -+#define IOREGION_OFFSET 5 -+#define IOREGION_LENGTH 2 -+#define ADDR_REG_OFFSET 0 -+#define DATA_REG_OFFSET 1 -+ -+#define NCT6775_REG_BANK 0x4E -+#define NCT6775_REG_CONFIG 0x40 -+ -+/* -+ * Not currently used: -+ * REG_MAN_ID has the value 0x5ca3 for all supported chips. -+ * REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model. -+ * REG_MAN_ID is at port 0x4f -+ * REG_CHIP_ID is at port 0x58 -+ */ -+ -+#define NUM_REG_ALARM 4 /* Max number of alarm registers */ -+ -+/* Common and NCT6775 specific data */ -+ -+/* Voltage min/max registers for nr=7..14 are in bank 5 */ -+ -+static const u16 NCT6775_REG_IN_MAX[] = { -+ 0x2b, 0x2d, 0x2f, 0x31, 0x33, 0x35, 0x37, 0x554, 0x556, 0x558, 0x55a, -+ 0x55c, 0x55e, 0x560, 0x562 }; -+static const u16 NCT6775_REG_IN_MIN[] = { -+ 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x555, 0x557, 0x559, 0x55b, -+ 0x55d, 0x55f, 0x561, 0x563 }; -+static const u16 NCT6775_REG_IN[] = { -+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x550, 0x551, 0x552 -+}; -+ -+#define NCT6775_REG_VBAT 0x5D -+ -+static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B }; -+ -+/* 0..15 voltages, 16..23 fans, 24..31 temperatures */ -+ -+static const s8 NCT6775_ALARM_BITS[] = { -+ 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ -+ 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */ -+ -1, /* unused */ -+ 6, 7, 11, 10, 23, /* fan1..fan5 */ -+ -1, -1, -1, /* unused */ -+ 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ -+ 12, -1 }; /* intrusion0, intrusion1 */ -+ -+/* NCT6776 specific data */ -+ -+static const s8 NCT6776_ALARM_BITS[] = { -+ 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ -+ 17, -1, -1, -1, -1, -1, -1, /* in8..in14 */ -+ -1, /* unused */ -+ 6, 7, 11, 10, 23, /* fan1..fan5 */ -+ -1, -1, -1, /* unused */ -+ 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ -+ 12, 9 }; /* intrusion0, intrusion1 */ -+ -+/* NCT6779 specific data */ -+ -+static const u16 NCT6779_REG_IN[] = { -+ 0x480, 0x481, 0x482, 0x483, 0x484, 0x485, 0x486, 0x487, -+ 0x488, 0x489, 0x48a, 0x48b, 0x48c, 0x48d, 0x48e }; -+ -+static const u16 NCT6779_REG_ALARM[NUM_REG_ALARM] = { -+ 0x459, 0x45A, 0x45B, 0x568 }; -+ -+static const s8 NCT6779_ALARM_BITS[] = { -+ 0, 1, 2, 3, 8, 21, 20, 16, /* in0.. in7 */ -+ 17, 24, 25, 26, 27, 28, 29, /* in8..in14 */ -+ -1, /* unused */ -+ 6, 7, 11, 10, 23, /* fan1..fan5 */ -+ -1, -1, -1, /* unused */ -+ 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ -+ 12, 9 }; /* intrusion0, intrusion1 */ -+ -+/* -+ * Conversions -+ */ -+ -+/* -+ * Some of the voltage inputs have internal scaling, the tables below -+ * contain 8 (the ADC LSB in mV) * scaling factor * 100 -+ */ -+static const u16 scale_in[15] = { -+ 800, 800, 1600, 1600, 800, 800, 800, 1600, 1600, 800, 800, 800, 800, -+ 800, 800 -+}; -+ -+static inline long in_from_reg(u8 reg, u8 nr) -+{ -+ return DIV_ROUND_CLOSEST(reg * scale_in[nr], 100); -+} -+ -+static inline u8 in_to_reg(u32 val, u8 nr) -+{ -+ return clamp_val(DIV_ROUND_CLOSEST(val * 100, scale_in[nr]), 0, 255); -+} -+ -+/* -+ * Data structures and manipulation thereof -+ */ -+ -+struct nct6775_data { -+ int addr; /* IO base of hw monitor block */ -+ enum kinds kind; -+ const char *name; -+ -+ struct device *hwmon_dev; -+ struct mutex lock; -+ -+ u16 REG_CONFIG; -+ u16 REG_VBAT; -+ -+ const s8 *ALARM_BITS; -+ -+ const u16 *REG_VIN; -+ const u16 *REG_IN_MINMAX[2]; -+ -+ const u16 *REG_ALARM; -+ -+ struct mutex update_lock; -+ bool valid; /* true if following fields are valid */ -+ unsigned long last_updated; /* In jiffies */ -+ -+ /* Register values */ -+ u8 bank; /* current register bank */ -+ u8 in_num; /* number of in inputs we have */ -+ u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */ -+ -+ u64 alarms; -+ -+ u8 vid; -+ u8 vrm; -+ -+ u16 have_in; -+}; -+ -+struct nct6775_sio_data { -+ int sioreg; -+ enum kinds kind; -+}; -+ -+static bool is_word_sized(struct nct6775_data *data, u16 reg) -+{ -+ switch (data->kind) { -+ case nct6775: -+ return (((reg & 0xff00) == 0x100 || -+ (reg & 0xff00) == 0x200) && -+ ((reg & 0x00ff) == 0x50 || -+ (reg & 0x00ff) == 0x53 || -+ (reg & 0x00ff) == 0x55)) || -+ (reg & 0xfff0) == 0x630 || -+ reg == 0x640 || reg == 0x642 || -+ reg == 0x662 || -+ ((reg & 0xfff0) == 0x650 && (reg & 0x000f) >= 0x06) || -+ reg == 0x73 || reg == 0x75 || reg == 0x77; -+ case nct6776: -+ return (((reg & 0xff00) == 0x100 || -+ (reg & 0xff00) == 0x200) && -+ ((reg & 0x00ff) == 0x50 || -+ (reg & 0x00ff) == 0x53 || -+ (reg & 0x00ff) == 0x55)) || -+ (reg & 0xfff0) == 0x630 || -+ reg == 0x402 || -+ reg == 0x640 || reg == 0x642 || -+ ((reg & 0xfff0) == 0x650 && (reg & 0x000f) >= 0x06) || -+ reg == 0x73 || reg == 0x75 || reg == 0x77; -+ case nct6779: -+ return reg == 0x150 || reg == 0x153 || reg == 0x155 || -+ ((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x09) || -+ reg == 0x402 || -+ reg == 0x63a || reg == 0x63c || reg == 0x63e || -+ reg == 0x640 || reg == 0x642 || -+ reg == 0x73 || reg == 0x75 || reg == 0x77 || reg == 0x79 || -+ reg == 0x7b; -+ } -+ return false; -+} -+ -+/* -+ * On older chips, only registers 0x50-0x5f are banked. -+ * On more recent chips, all registers are banked. -+ * Assume that is the case and set the bank number for each access. -+ * Cache the bank number so it only needs to be set if it changes. -+ */ -+static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg) -+{ -+ u8 bank = reg >> 8; -+ if (data->bank != bank) { -+ outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET); -+ outb_p(bank, data->addr + DATA_REG_OFFSET); -+ data->bank = bank; -+ } -+} -+ -+static u16 nct6775_read_value(struct nct6775_data *data, u16 reg) -+{ -+ int res, word_sized = is_word_sized(data, reg); -+ -+ mutex_lock(&data->lock); -+ -+ nct6775_set_bank(data, reg); -+ outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET); -+ res = inb_p(data->addr + DATA_REG_OFFSET); -+ if (word_sized) { -+ outb_p((reg & 0xff) + 1, -+ data->addr + ADDR_REG_OFFSET); -+ res = (res << 8) + inb_p(data->addr + DATA_REG_OFFSET); -+ } -+ -+ mutex_unlock(&data->lock); -+ return res; -+} -+ -+static int nct6775_write_value(struct nct6775_data *data, u16 reg, u16 value) -+{ -+ int word_sized = is_word_sized(data, reg); -+ -+ mutex_lock(&data->lock); -+ -+ nct6775_set_bank(data, reg); -+ outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET); -+ if (word_sized) { -+ outb_p(value >> 8, data->addr + DATA_REG_OFFSET); -+ outb_p((reg & 0xff) + 1, -+ data->addr + ADDR_REG_OFFSET); -+ } -+ outb_p(value & 0xff, data->addr + DATA_REG_OFFSET); -+ -+ mutex_unlock(&data->lock); -+ return 0; -+} -+ -+static struct nct6775_data *nct6775_update_device(struct device *dev) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ int i; -+ -+ mutex_lock(&data->update_lock); -+ -+ if (time_after(jiffies, data->last_updated + HZ + HZ/2) -+ || !data->valid) { -+ /* Measured voltages and limits */ -+ for (i = 0; i < data->in_num; i++) { -+ if (!(data->have_in & (1 << i))) -+ continue; -+ -+ data->in[i][0] = nct6775_read_value(data, -+ data->REG_VIN[i]); -+ data->in[i][1] = nct6775_read_value(data, -+ data->REG_IN_MINMAX[0][i]); -+ data->in[i][2] = nct6775_read_value(data, -+ data->REG_IN_MINMAX[1][i]); -+ } -+ -+ data->alarms = 0; -+ for (i = 0; i < NUM_REG_ALARM; i++) { -+ u8 alarm; -+ if (!data->REG_ALARM[i]) -+ continue; -+ alarm = nct6775_read_value(data, data->REG_ALARM[i]); -+ data->alarms |= ((u64)alarm) << (i << 3); -+ } -+ -+ data->last_updated = jiffies; -+ data->valid = true; -+ } -+ -+ mutex_unlock(&data->update_lock); -+ return data; -+} -+ -+/* -+ * Sysfs callback functions -+ */ -+static ssize_t -+show_in_reg(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); -+ int nr = sattr->nr; -+ int index = sattr->index; -+ return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr)); -+} -+ -+static ssize_t -+store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf, -+ size_t count) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); -+ int nr = sattr->nr; -+ int index = sattr->index; -+ unsigned long val; -+ int err = kstrtoul(buf, 10, &val); -+ if (err < 0) -+ return err; -+ mutex_lock(&data->update_lock); -+ data->in[nr][index] = in_to_reg(val, nr); -+ nct6775_write_value(data, data->REG_IN_MINMAX[index-1][nr], -+ data->in[nr][index]); -+ mutex_unlock(&data->update_lock); -+ return count; -+} -+ -+static ssize_t -+show_alarm(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = data->ALARM_BITS[sattr->index]; -+ return sprintf(buf, "%u\n", -+ (unsigned int)((data->alarms >> nr) & 0x01)); -+} -+ -+static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in_reg, NULL, 0, 0); -+static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in_reg, NULL, 1, 0); -+static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in_reg, NULL, 2, 0); -+static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in_reg, NULL, 3, 0); -+static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in_reg, NULL, 4, 0); -+static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_in_reg, NULL, 5, 0); -+static SENSOR_DEVICE_ATTR_2(in6_input, S_IRUGO, show_in_reg, NULL, 6, 0); -+static SENSOR_DEVICE_ATTR_2(in7_input, S_IRUGO, show_in_reg, NULL, 7, 0); -+static SENSOR_DEVICE_ATTR_2(in8_input, S_IRUGO, show_in_reg, NULL, 8, 0); -+static SENSOR_DEVICE_ATTR_2(in9_input, S_IRUGO, show_in_reg, NULL, 9, 0); -+static SENSOR_DEVICE_ATTR_2(in10_input, S_IRUGO, show_in_reg, NULL, 10, 0); -+static SENSOR_DEVICE_ATTR_2(in11_input, S_IRUGO, show_in_reg, NULL, 11, 0); -+static SENSOR_DEVICE_ATTR_2(in12_input, S_IRUGO, show_in_reg, NULL, 12, 0); -+static SENSOR_DEVICE_ATTR_2(in13_input, S_IRUGO, show_in_reg, NULL, 13, 0); -+static SENSOR_DEVICE_ATTR_2(in14_input, S_IRUGO, show_in_reg, NULL, 14, 0); -+ -+static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); -+static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); -+static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2); -+static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3); -+static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 4); -+static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 5); -+static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 6); -+static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 7); -+static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 8); -+static SENSOR_DEVICE_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 9); -+static SENSOR_DEVICE_ATTR(in10_alarm, S_IRUGO, show_alarm, NULL, 10); -+static SENSOR_DEVICE_ATTR(in11_alarm, S_IRUGO, show_alarm, NULL, 11); -+static SENSOR_DEVICE_ATTR(in12_alarm, S_IRUGO, show_alarm, NULL, 12); -+static SENSOR_DEVICE_ATTR(in13_alarm, S_IRUGO, show_alarm, NULL, 13); -+static SENSOR_DEVICE_ATTR(in14_alarm, S_IRUGO, show_alarm, NULL, 14); -+ -+static SENSOR_DEVICE_ATTR_2(in0_min, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 0, 1); -+static SENSOR_DEVICE_ATTR_2(in1_min, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 1, 1); -+static SENSOR_DEVICE_ATTR_2(in2_min, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 2, 1); -+static SENSOR_DEVICE_ATTR_2(in3_min, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 3, 1); -+static SENSOR_DEVICE_ATTR_2(in4_min, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 4, 1); -+static SENSOR_DEVICE_ATTR_2(in5_min, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 5, 1); -+static SENSOR_DEVICE_ATTR_2(in6_min, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 6, 1); -+static SENSOR_DEVICE_ATTR_2(in7_min, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 7, 1); -+static SENSOR_DEVICE_ATTR_2(in8_min, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 8, 1); -+static SENSOR_DEVICE_ATTR_2(in9_min, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 9, 1); -+static SENSOR_DEVICE_ATTR_2(in10_min, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 10, 1); -+static SENSOR_DEVICE_ATTR_2(in11_min, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 11, 1); -+static SENSOR_DEVICE_ATTR_2(in12_min, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 12, 1); -+static SENSOR_DEVICE_ATTR_2(in13_min, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 13, 1); -+static SENSOR_DEVICE_ATTR_2(in14_min, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 14, 1); -+ -+static SENSOR_DEVICE_ATTR_2(in0_max, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 0, 2); -+static SENSOR_DEVICE_ATTR_2(in1_max, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 1, 2); -+static SENSOR_DEVICE_ATTR_2(in2_max, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 2, 2); -+static SENSOR_DEVICE_ATTR_2(in3_max, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 3, 2); -+static SENSOR_DEVICE_ATTR_2(in4_max, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 4, 2); -+static SENSOR_DEVICE_ATTR_2(in5_max, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 5, 2); -+static SENSOR_DEVICE_ATTR_2(in6_max, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 6, 2); -+static SENSOR_DEVICE_ATTR_2(in7_max, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 7, 2); -+static SENSOR_DEVICE_ATTR_2(in8_max, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 8, 2); -+static SENSOR_DEVICE_ATTR_2(in9_max, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 9, 2); -+static SENSOR_DEVICE_ATTR_2(in10_max, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 10, 2); -+static SENSOR_DEVICE_ATTR_2(in11_max, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 11, 2); -+static SENSOR_DEVICE_ATTR_2(in12_max, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 12, 2); -+static SENSOR_DEVICE_ATTR_2(in13_max, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 13, 2); -+static SENSOR_DEVICE_ATTR_2(in14_max, S_IWUSR | S_IRUGO, show_in_reg, -+ store_in_reg, 14, 2); -+ -+static struct attribute *nct6775_attributes_in[15][5] = { -+ { -+ &sensor_dev_attr_in0_input.dev_attr.attr, -+ &sensor_dev_attr_in0_min.dev_attr.attr, -+ &sensor_dev_attr_in0_max.dev_attr.attr, -+ &sensor_dev_attr_in0_alarm.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_in1_input.dev_attr.attr, -+ &sensor_dev_attr_in1_min.dev_attr.attr, -+ &sensor_dev_attr_in1_max.dev_attr.attr, -+ &sensor_dev_attr_in1_alarm.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_in2_input.dev_attr.attr, -+ &sensor_dev_attr_in2_min.dev_attr.attr, -+ &sensor_dev_attr_in2_max.dev_attr.attr, -+ &sensor_dev_attr_in2_alarm.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_in3_input.dev_attr.attr, -+ &sensor_dev_attr_in3_min.dev_attr.attr, -+ &sensor_dev_attr_in3_max.dev_attr.attr, -+ &sensor_dev_attr_in3_alarm.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_in4_input.dev_attr.attr, -+ &sensor_dev_attr_in4_min.dev_attr.attr, -+ &sensor_dev_attr_in4_max.dev_attr.attr, -+ &sensor_dev_attr_in4_alarm.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_in5_input.dev_attr.attr, -+ &sensor_dev_attr_in5_min.dev_attr.attr, -+ &sensor_dev_attr_in5_max.dev_attr.attr, -+ &sensor_dev_attr_in5_alarm.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_in6_input.dev_attr.attr, -+ &sensor_dev_attr_in6_min.dev_attr.attr, -+ &sensor_dev_attr_in6_max.dev_attr.attr, -+ &sensor_dev_attr_in6_alarm.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_in7_input.dev_attr.attr, -+ &sensor_dev_attr_in7_min.dev_attr.attr, -+ &sensor_dev_attr_in7_max.dev_attr.attr, -+ &sensor_dev_attr_in7_alarm.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_in8_input.dev_attr.attr, -+ &sensor_dev_attr_in8_min.dev_attr.attr, -+ &sensor_dev_attr_in8_max.dev_attr.attr, -+ &sensor_dev_attr_in8_alarm.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_in9_input.dev_attr.attr, -+ &sensor_dev_attr_in9_min.dev_attr.attr, -+ &sensor_dev_attr_in9_max.dev_attr.attr, -+ &sensor_dev_attr_in9_alarm.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_in10_input.dev_attr.attr, -+ &sensor_dev_attr_in10_min.dev_attr.attr, -+ &sensor_dev_attr_in10_max.dev_attr.attr, -+ &sensor_dev_attr_in10_alarm.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_in11_input.dev_attr.attr, -+ &sensor_dev_attr_in11_min.dev_attr.attr, -+ &sensor_dev_attr_in11_max.dev_attr.attr, -+ &sensor_dev_attr_in11_alarm.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_in12_input.dev_attr.attr, -+ &sensor_dev_attr_in12_min.dev_attr.attr, -+ &sensor_dev_attr_in12_max.dev_attr.attr, -+ &sensor_dev_attr_in12_alarm.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_in13_input.dev_attr.attr, -+ &sensor_dev_attr_in13_min.dev_attr.attr, -+ &sensor_dev_attr_in13_max.dev_attr.attr, -+ &sensor_dev_attr_in13_alarm.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_in14_input.dev_attr.attr, -+ &sensor_dev_attr_in14_min.dev_attr.attr, -+ &sensor_dev_attr_in14_max.dev_attr.attr, -+ &sensor_dev_attr_in14_alarm.dev_attr.attr, -+ NULL -+ }, -+}; -+ -+static const struct attribute_group nct6775_group_in[15] = { -+ { .attrs = nct6775_attributes_in[0] }, -+ { .attrs = nct6775_attributes_in[1] }, -+ { .attrs = nct6775_attributes_in[2] }, -+ { .attrs = nct6775_attributes_in[3] }, -+ { .attrs = nct6775_attributes_in[4] }, -+ { .attrs = nct6775_attributes_in[5] }, -+ { .attrs = nct6775_attributes_in[6] }, -+ { .attrs = nct6775_attributes_in[7] }, -+ { .attrs = nct6775_attributes_in[8] }, -+ { .attrs = nct6775_attributes_in[9] }, -+ { .attrs = nct6775_attributes_in[10] }, -+ { .attrs = nct6775_attributes_in[11] }, -+ { .attrs = nct6775_attributes_in[12] }, -+ { .attrs = nct6775_attributes_in[13] }, -+ { .attrs = nct6775_attributes_in[14] }, -+}; -+ -+static ssize_t -+show_name(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ -+ return sprintf(buf, "%s\n", data->name); -+} -+ -+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); -+ -+static ssize_t -+show_vid(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); -+} -+ -+static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); -+ -+/* -+ * Driver and device management -+ */ -+ -+static void nct6775_device_remove_files(struct device *dev) -+{ -+ /* -+ * some entries in the following arrays may not have been used in -+ * device_create_file(), but device_remove_file() will ignore them -+ */ -+ int i; -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ -+ for (i = 0; i < data->in_num; i++) -+ sysfs_remove_group(&dev->kobj, &nct6775_group_in[i]); -+ -+ device_remove_file(dev, &dev_attr_name); -+ device_remove_file(dev, &dev_attr_cpu0_vid); -+} -+ -+/* Get the monitoring functions started */ -+static inline void nct6775_init_device(struct nct6775_data *data) -+{ -+ u8 tmp; -+ -+ /* Start monitoring if needed */ -+ if (data->REG_CONFIG) { -+ tmp = nct6775_read_value(data, data->REG_CONFIG); -+ if (!(tmp & 0x01)) -+ nct6775_write_value(data, data->REG_CONFIG, tmp | 0x01); -+ } -+ -+ /* Enable VBAT monitoring if needed */ -+ tmp = nct6775_read_value(data, data->REG_VBAT); -+ if (!(tmp & 0x01)) -+ nct6775_write_value(data, data->REG_VBAT, tmp | 0x01); -+} -+ -+static int nct6775_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ struct nct6775_sio_data *sio_data = dev->platform_data; -+ struct nct6775_data *data; -+ struct resource *res; -+ int i, err = 0; -+ -+ res = platform_get_resource(pdev, IORESOURCE_IO, 0); -+ if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH, -+ DRVNAME)) -+ return -EBUSY; -+ -+ data = devm_kzalloc(&pdev->dev, sizeof(struct nct6775_data), -+ GFP_KERNEL); -+ if (!data) -+ return -ENOMEM; -+ -+ data->kind = sio_data->kind; -+ data->addr = res->start; -+ mutex_init(&data->lock); -+ mutex_init(&data->update_lock); -+ data->name = nct6775_device_names[data->kind]; -+ data->bank = 0xff; /* Force initial bank selection */ -+ platform_set_drvdata(pdev, data); -+ -+ switch (data->kind) { -+ case nct6775: -+ data->in_num = 9; -+ -+ data->ALARM_BITS = NCT6775_ALARM_BITS; -+ -+ data->REG_CONFIG = NCT6775_REG_CONFIG; -+ data->REG_VBAT = NCT6775_REG_VBAT; -+ data->REG_VIN = NCT6775_REG_IN; -+ data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; -+ data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; -+ data->REG_ALARM = NCT6775_REG_ALARM; -+ break; -+ case nct6776: -+ data->in_num = 9; -+ -+ data->ALARM_BITS = NCT6776_ALARM_BITS; -+ -+ data->REG_CONFIG = NCT6775_REG_CONFIG; -+ data->REG_VBAT = NCT6775_REG_VBAT; -+ data->REG_VIN = NCT6775_REG_IN; -+ data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; -+ data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; -+ data->REG_ALARM = NCT6775_REG_ALARM; -+ break; -+ case nct6779: -+ data->in_num = 15; -+ -+ data->ALARM_BITS = NCT6779_ALARM_BITS; -+ -+ data->REG_CONFIG = NCT6775_REG_CONFIG; -+ data->REG_VBAT = NCT6775_REG_VBAT; -+ data->REG_VIN = NCT6779_REG_IN; -+ data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; -+ data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; -+ data->REG_ALARM = NCT6779_REG_ALARM; -+ break; -+ default: -+ return -ENODEV; -+ } -+ data->have_in = (1 << data->in_num) - 1; -+ -+ /* Initialize the chip */ -+ nct6775_init_device(data); -+ -+ data->vrm = vid_which_vrm(); -+ err = superio_enter(sio_data->sioreg); -+ if (err) -+ return err; -+ -+ /* -+ * Read VID value -+ * We can get the VID input values directly at logical device D 0xe3. -+ */ -+ superio_select(sio_data->sioreg, NCT6775_LD_VID); -+ data->vid = superio_inb(sio_data->sioreg, 0xe3); -+ superio_exit(sio_data->sioreg); -+ -+ err = device_create_file(dev, &dev_attr_cpu0_vid); -+ if (err) -+ return err; -+ -+ for (i = 0; i < data->in_num; i++) { -+ if (!(data->have_in & (1 << i))) -+ continue; -+ err = sysfs_create_group(&dev->kobj, &nct6775_group_in[i]); -+ if (err) -+ goto exit_remove; -+ } -+ -+ err = device_create_file(dev, &dev_attr_name); -+ if (err) -+ goto exit_remove; -+ -+ data->hwmon_dev = hwmon_device_register(dev); -+ if (IS_ERR(data->hwmon_dev)) { -+ err = PTR_ERR(data->hwmon_dev); -+ goto exit_remove; -+ } -+ -+ return 0; -+ -+exit_remove: -+ nct6775_device_remove_files(dev); -+ return err; -+} -+ -+static int nct6775_remove(struct platform_device *pdev) -+{ -+ struct nct6775_data *data = platform_get_drvdata(pdev); -+ -+ hwmon_device_unregister(data->hwmon_dev); -+ nct6775_device_remove_files(&pdev->dev); -+ -+ return 0; -+} -+ -+static struct platform_driver nct6775_driver = { -+ .driver = { -+ .owner = THIS_MODULE, -+ .name = DRVNAME, -+ }, -+ .probe = nct6775_probe, -+ .remove = nct6775_remove, -+}; -+ -+/* nct6775_find() looks for a '627 in the Super-I/O config space */ -+static int __init nct6775_find(int sioaddr, unsigned short *addr, -+ struct nct6775_sio_data *sio_data) -+{ -+ static const char sio_name_NCT6775[] __initconst = "NCT6775F"; -+ static const char sio_name_NCT6776[] __initconst = "NCT6776F"; -+ static const char sio_name_NCT6779[] __initconst = "NCT6779D"; -+ -+ u16 val; -+ const char *sio_name; -+ int err; -+ -+ err = superio_enter(sioaddr); -+ if (err) -+ return err; -+ -+ if (force_id) -+ val = force_id; -+ else -+ val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8) -+ | superio_inb(sioaddr, SIO_REG_DEVID + 1); -+ switch (val & SIO_ID_MASK) { -+ case SIO_NCT6775_ID: -+ sio_data->kind = nct6775; -+ sio_name = sio_name_NCT6775; -+ break; -+ case SIO_NCT6776_ID: -+ sio_data->kind = nct6776; -+ sio_name = sio_name_NCT6776; -+ break; -+ case SIO_NCT6779_ID: -+ sio_data->kind = nct6779; -+ sio_name = sio_name_NCT6779; -+ break; -+ default: -+ if (val != 0xffff) -+ pr_debug("unsupported chip ID: 0x%04x\n", val); -+ superio_exit(sioaddr); -+ return -ENODEV; -+ } -+ -+ /* We have a known chip, find the HWM I/O address */ -+ superio_select(sioaddr, NCT6775_LD_HWM); -+ val = (superio_inb(sioaddr, SIO_REG_ADDR) << 8) -+ | superio_inb(sioaddr, SIO_REG_ADDR + 1); -+ *addr = val & IOREGION_ALIGNMENT; -+ if (*addr == 0) { -+ pr_err("Refusing to enable a Super-I/O device with a base I/O port 0\n"); -+ superio_exit(sioaddr); -+ return -ENODEV; -+ } -+ -+ /* Activate logical device if needed */ -+ val = superio_inb(sioaddr, SIO_REG_ENABLE); -+ if (!(val & 0x01)) { -+ pr_warn("Forcibly enabling Super-I/O. Sensor is probably unusable.\n"); -+ superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01); -+ } -+ -+ superio_exit(sioaddr); -+ pr_info("Found %s chip at %#x\n", sio_name, *addr); -+ sio_data->sioreg = sioaddr; -+ -+ return 0; -+} -+ -+/* -+ * when Super-I/O functions move to a separate file, the Super-I/O -+ * bus will manage the lifetime of the device and this module will only keep -+ * track of the nct6775 driver. But since we platform_device_alloc(), we -+ * must keep track of the device -+ */ -+static struct platform_device *pdev; -+ -+static int __init sensors_nct6775_init(void) -+{ -+ int err; -+ unsigned short address; -+ struct resource res; -+ struct nct6775_sio_data sio_data; -+ -+ /* -+ * initialize sio_data->kind and sio_data->sioreg. -+ * -+ * when Super-I/O functions move to a separate file, the Super-I/O -+ * driver will probe 0x2e and 0x4e and auto-detect the presence of a -+ * nct6775 hardware monitor, and call probe() -+ */ -+ if (nct6775_find(0x2e, &address, &sio_data) && -+ nct6775_find(0x4e, &address, &sio_data)) -+ return -ENODEV; -+ -+ err = platform_driver_register(&nct6775_driver); -+ if (err) -+ goto exit; -+ -+ pdev = platform_device_alloc(DRVNAME, address); -+ if (!pdev) { -+ err = -ENOMEM; -+ pr_err("Device allocation failed\n"); -+ goto exit_unregister; -+ } -+ -+ err = platform_device_add_data(pdev, &sio_data, -+ sizeof(struct nct6775_sio_data)); -+ if (err) { -+ pr_err("Platform data allocation failed\n"); -+ goto exit_device_put; -+ } -+ -+ memset(&res, 0, sizeof(res)); -+ res.name = DRVNAME; -+ res.start = address + IOREGION_OFFSET; -+ res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1; -+ res.flags = IORESOURCE_IO; -+ -+ err = acpi_check_resource_conflict(&res); -+ if (err) -+ goto exit_device_put; -+ -+ err = platform_device_add_resources(pdev, &res, 1); -+ if (err) { -+ pr_err("Device resource addition failed (%d)\n", err); -+ goto exit_device_put; -+ } -+ -+ /* platform_device_add calls probe() */ -+ err = platform_device_add(pdev); -+ if (err) { -+ pr_err("Device addition failed (%d)\n", err); -+ goto exit_device_put; -+ } -+ -+ return 0; -+ -+exit_device_put: -+ platform_device_put(pdev); -+exit_unregister: -+ platform_driver_unregister(&nct6775_driver); -+exit: -+ return err; -+} -+ -+static void __exit sensors_nct6775_exit(void) -+{ -+ platform_device_unregister(pdev); -+ platform_driver_unregister(&nct6775_driver); -+} -+ -+MODULE_AUTHOR("Guenter Roeck "); -+MODULE_DESCRIPTION("NCT6775F/NCT6776F/NCT6779D driver"); -+MODULE_LICENSE("GPL"); -+ -+module_init(sensors_nct6775_init); -+module_exit(sensors_nct6775_exit); - -From a6bd587842772cd3e63a689c7ff4d64cf25284a3 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Tue, 4 Dec 2012 03:13:34 -0800 -Subject: [PATCH] hwmon: (nct6775) Add case open detection - -Signed-off-by: Guenter Roeck - -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index f75cd82..435691f 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -76,6 +76,7 @@ MODULE_PARM_DESC(force_id, "Override the detected device ID"); - * Super-I/O constants and functions - */ - -+#define NCT6775_LD_ACPI 0x0a - #define NCT6775_LD_HWM 0x0b - #define NCT6775_LD_VID 0x0d - -@@ -186,6 +187,11 @@ static const s8 NCT6775_ALARM_BITS[] = { - 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ - 12, -1 }; /* intrusion0, intrusion1 */ - -+#define INTRUSION_ALARM_BASE 30 -+ -+static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee }; -+static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 }; -+ - /* NCT6776 specific data */ - - static const s8 NCT6776_ALARM_BITS[] = { -@@ -694,6 +700,56 @@ show_vid(struct device *dev, struct device_attribute *attr, char *buf) - - static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); - -+/* Case open detection */ -+ -+static ssize_t -+clear_caseopen(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct nct6775_sio_data *sio_data = dev->platform_data; -+ int nr = to_sensor_dev_attr(attr)->index - INTRUSION_ALARM_BASE; -+ unsigned long val; -+ u8 reg; -+ int ret; -+ -+ if (kstrtoul(buf, 10, &val) || val != 0) -+ return -EINVAL; -+ -+ mutex_lock(&data->update_lock); -+ -+ /* -+ * Use CR registers to clear caseopen status. -+ * The CR registers are the same for all chips, and not all chips -+ * support clearing the caseopen status through "regular" registers. -+ */ -+ ret = superio_enter(sio_data->sioreg); -+ if (ret) { -+ count = ret; -+ goto error; -+ } -+ -+ superio_select(sio_data->sioreg, NCT6775_LD_ACPI); -+ reg = superio_inb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr]); -+ reg |= NCT6775_CR_CASEOPEN_CLR_MASK[nr]; -+ superio_outb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg); -+ reg &= ~NCT6775_CR_CASEOPEN_CLR_MASK[nr]; -+ superio_outb(sio_data->sioreg, NCT6775_REG_CR_CASEOPEN_CLR[nr], reg); -+ superio_exit(sio_data->sioreg); -+ -+ data->valid = false; /* Force cache refresh */ -+error: -+ mutex_unlock(&data->update_lock); -+ return count; -+} -+ -+static struct sensor_device_attribute sda_caseopen[] = { -+ SENSOR_ATTR(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm, -+ clear_caseopen, INTRUSION_ALARM_BASE), -+ SENSOR_ATTR(intrusion1_alarm, S_IWUSR | S_IRUGO, show_alarm, -+ clear_caseopen, INTRUSION_ALARM_BASE + 1), -+}; -+ - /* - * Driver and device management - */ -@@ -710,6 +766,9 @@ static void nct6775_device_remove_files(struct device *dev) - for (i = 0; i < data->in_num; i++) - sysfs_remove_group(&dev->kobj, &nct6775_group_in[i]); - -+ device_remove_file(dev, &sda_caseopen[0].dev_attr); -+ device_remove_file(dev, &sda_caseopen[1].dev_attr); -+ - device_remove_file(dev, &dev_attr_name); - device_remove_file(dev, &dev_attr_cpu0_vid); - } -@@ -828,6 +887,14 @@ static int nct6775_probe(struct platform_device *pdev) - goto exit_remove; - } - -+ for (i = 0; i < ARRAY_SIZE(sda_caseopen); i++) { -+ if (data->ALARM_BITS[INTRUSION_ALARM_BASE + i] < 0) -+ continue; -+ err = device_create_file(dev, &sda_caseopen[i].dev_attr); -+ if (err) -+ goto exit_remove; -+ } -+ - err = device_create_file(dev, &dev_attr_name); - if (err) - goto exit_remove; - -From aa136e5dad9fbec9e98867278555a81f2d75ea10 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Tue, 4 Dec 2012 03:26:05 -0800 -Subject: [PATCH] hwmon: (nct6775) Add support for temperature sensors - -Signed-off-by: Guenter Roeck - -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index 435691f..fd0dd15 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -57,6 +57,8 @@ - #include - #include "lm75.h" - -+#define USE_ALTERNATE -+ - enum kinds { nct6775, nct6776, nct6779 }; - - /* used to set data->name = nct6775_device_names[data->sio_kind] */ -@@ -156,6 +158,9 @@ superio_exit(int ioreg) - * REG_CHIP_ID is at port 0x58 - */ - -+#define NUM_TEMP 10 /* Max number of temp attribute sets w/ limits*/ -+#define NUM_TEMP_FIXED 6 /* Max number of fixed temp attribute sets */ -+ - #define NUM_REG_ALARM 4 /* Max number of alarm registers */ - - /* Common and NCT6775 specific data */ -@@ -173,6 +178,7 @@ static const u16 NCT6775_REG_IN[] = { - }; - - #define NCT6775_REG_VBAT 0x5D -+#define NCT6775_REG_DIODE 0x5E - - static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B }; - -@@ -187,11 +193,58 @@ static const s8 NCT6775_ALARM_BITS[] = { - 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ - 12, -1 }; /* intrusion0, intrusion1 */ - -+#define TEMP_ALARM_BASE 24 - #define INTRUSION_ALARM_BASE 30 - - static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee }; - static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 }; - -+static const u16 NCT6775_REG_TEMP[] = { -+ 0x27, 0x150, 0x250, 0x62b, 0x62c, 0x62d }; -+ -+static const u16 NCT6775_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = { -+ 0, 0x152, 0x252, 0x628, 0x629, 0x62A }; -+static const u16 NCT6775_REG_TEMP_HYST[ARRAY_SIZE(NCT6775_REG_TEMP)] = { -+ 0x3a, 0x153, 0x253, 0x673, 0x678, 0x67D }; -+static const u16 NCT6775_REG_TEMP_OVER[ARRAY_SIZE(NCT6775_REG_TEMP)] = { -+ 0x39, 0x155, 0x255, 0x672, 0x677, 0x67C }; -+ -+static const u16 NCT6775_REG_TEMP_SOURCE[ARRAY_SIZE(NCT6775_REG_TEMP)] = { -+ 0x621, 0x622, 0x623, 0x624, 0x625, 0x626 }; -+ -+static const u16 NCT6775_REG_TEMP_OFFSET[] = { 0x454, 0x455, 0x456 }; -+ -+static const char *const nct6775_temp_label[] = { -+ "", -+ "SYSTIN", -+ "CPUTIN", -+ "AUXTIN", -+ "AMD SB-TSI", -+ "PECI Agent 0", -+ "PECI Agent 1", -+ "PECI Agent 2", -+ "PECI Agent 3", -+ "PECI Agent 4", -+ "PECI Agent 5", -+ "PECI Agent 6", -+ "PECI Agent 7", -+ "PCH_CHIP_CPU_MAX_TEMP", -+ "PCH_CHIP_TEMP", -+ "PCH_CPU_TEMP", -+ "PCH_MCH_TEMP", -+ "PCH_DIM0_TEMP", -+ "PCH_DIM1_TEMP", -+ "PCH_DIM2_TEMP", -+ "PCH_DIM3_TEMP" -+}; -+ -+static const u16 NCT6775_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6775_temp_label) - 1] -+ = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x661, 0x662, 0x664 }; -+ -+static const u16 NCT6775_REG_TEMP_CRIT[ARRAY_SIZE(nct6775_temp_label) - 1] -+ = { 0, 0, 0, 0, 0xa00, 0xa01, 0xa02, 0xa03, 0xa04, 0xa05, 0xa06, -+ 0xa07 }; -+ - /* NCT6776 specific data */ - - static const s8 NCT6776_ALARM_BITS[] = { -@@ -203,6 +256,41 @@ static const s8 NCT6776_ALARM_BITS[] = { - 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ - 12, 9 }; /* intrusion0, intrusion1 */ - -+static const u16 NCT6776_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = { -+ 0x18, 0x152, 0x252, 0x628, 0x629, 0x62A }; -+ -+static const char *const nct6776_temp_label[] = { -+ "", -+ "SYSTIN", -+ "CPUTIN", -+ "AUXTIN", -+ "SMBUSMASTER 0", -+ "SMBUSMASTER 1", -+ "SMBUSMASTER 2", -+ "SMBUSMASTER 3", -+ "SMBUSMASTER 4", -+ "SMBUSMASTER 5", -+ "SMBUSMASTER 6", -+ "SMBUSMASTER 7", -+ "PECI Agent 0", -+ "PECI Agent 1", -+ "PCH_CHIP_CPU_MAX_TEMP", -+ "PCH_CHIP_TEMP", -+ "PCH_CPU_TEMP", -+ "PCH_MCH_TEMP", -+ "PCH_DIM0_TEMP", -+ "PCH_DIM1_TEMP", -+ "PCH_DIM2_TEMP", -+ "PCH_DIM3_TEMP", -+ "BYTE_TEMP" -+}; -+ -+static const u16 NCT6776_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6776_temp_label) - 1] -+ = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x401, 0x402, 0x404 }; -+ -+static const u16 NCT6776_REG_TEMP_CRIT[ARRAY_SIZE(nct6776_temp_label) - 1] -+ = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a }; -+ - /* NCT6779 specific data */ - - static const u16 NCT6779_REG_IN[] = { -@@ -221,6 +309,56 @@ static const s8 NCT6779_ALARM_BITS[] = { - 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ - 12, 9 }; /* intrusion0, intrusion1 */ - -+static const u16 NCT6779_REG_TEMP[] = { 0x27, 0x150 }; -+static const u16 NCT6779_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6779_REG_TEMP)] = { -+ 0x18, 0x152 }; -+static const u16 NCT6779_REG_TEMP_HYST[ARRAY_SIZE(NCT6779_REG_TEMP)] = { -+ 0x3a, 0x153 }; -+static const u16 NCT6779_REG_TEMP_OVER[ARRAY_SIZE(NCT6779_REG_TEMP)] = { -+ 0x39, 0x155 }; -+ -+static const u16 NCT6779_REG_TEMP_OFFSET[] = { -+ 0x454, 0x455, 0x456, 0x44a, 0x44b, 0x44c }; -+ -+static const char *const nct6779_temp_label[] = { -+ "", -+ "SYSTIN", -+ "CPUTIN", -+ "AUXTIN0", -+ "AUXTIN1", -+ "AUXTIN2", -+ "AUXTIN3", -+ "", -+ "SMBUSMASTER 0", -+ "SMBUSMASTER 1", -+ "SMBUSMASTER 2", -+ "SMBUSMASTER 3", -+ "SMBUSMASTER 4", -+ "SMBUSMASTER 5", -+ "SMBUSMASTER 6", -+ "SMBUSMASTER 7", -+ "PECI Agent 0", -+ "PECI Agent 1", -+ "PCH_CHIP_CPU_MAX_TEMP", -+ "PCH_CHIP_TEMP", -+ "PCH_CPU_TEMP", -+ "PCH_MCH_TEMP", -+ "PCH_DIM0_TEMP", -+ "PCH_DIM1_TEMP", -+ "PCH_DIM2_TEMP", -+ "PCH_DIM3_TEMP", -+ "BYTE_TEMP" -+}; -+ -+static const u16 NCT6779_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6779_temp_label) - 1] -+ = { 0x490, 0x491, 0x492, 0x493, 0x494, 0x495, 0, 0, -+ 0, 0, 0, 0, 0, 0, 0, 0, -+ 0, 0x400, 0x401, 0x402, 0x404, 0x405, 0x406, 0x407, -+ 0x408, 0 }; -+ -+static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1] -+ = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a }; -+ - /* - * Conversions - */ -@@ -256,14 +394,27 @@ struct nct6775_data { - struct device *hwmon_dev; - struct mutex lock; - -+ u16 reg_temp[4][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst, -+ * 3=temp_crit -+ */ -+ u8 temp_src[NUM_TEMP]; -+ u16 reg_temp_config[NUM_TEMP]; -+ const char * const *temp_label; -+ int temp_label_num; -+ - u16 REG_CONFIG; - u16 REG_VBAT; -+ u16 REG_DIODE; - - const s8 *ALARM_BITS; - - const u16 *REG_VIN; - const u16 *REG_IN_MINMAX[2]; - -+ const u16 *REG_TEMP_SOURCE; /* temp register sources */ -+ -+ const u16 *REG_TEMP_OFFSET; -+ - const u16 *REG_ALARM; - - struct mutex update_lock; -@@ -275,11 +426,18 @@ struct nct6775_data { - u8 in_num; /* number of in inputs we have */ - u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */ - -+ u8 temp_fixed_num; /* 3 or 6 */ -+ u8 temp_type[NUM_TEMP_FIXED]; -+ s8 temp_offset[NUM_TEMP_FIXED]; -+ s16 temp[4][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst, -+ * 3=temp_crit */ - u64 alarms; - - u8 vid; - u8 vrm; - -+ u16 have_temp; -+ u16 have_temp_fixed; - u16 have_in; - }; - -@@ -379,10 +537,29 @@ static int nct6775_write_value(struct nct6775_data *data, u16 reg, u16 value) - return 0; - } - -+/* We left-align 8-bit temperature values to make the code simpler */ -+static u16 nct6775_read_temp(struct nct6775_data *data, u16 reg) -+{ -+ u16 res; -+ -+ res = nct6775_read_value(data, reg); -+ if (!is_word_sized(data, reg)) -+ res <<= 8; -+ -+ return res; -+} -+ -+static int nct6775_write_temp(struct nct6775_data *data, u16 reg, u16 value) -+{ -+ if (!is_word_sized(data, reg)) -+ value >>= 8; -+ return nct6775_write_value(data, reg, value); -+} -+ - static struct nct6775_data *nct6775_update_device(struct device *dev) - { - struct nct6775_data *data = dev_get_drvdata(dev); -- int i; -+ int i, j; - - mutex_lock(&data->update_lock); - -@@ -401,6 +578,22 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) - data->REG_IN_MINMAX[1][i]); - } - -+ /* Measured temperatures and limits */ -+ for (i = 0; i < NUM_TEMP; i++) { -+ if (!(data->have_temp & (1 << i))) -+ continue; -+ for (j = 0; j < 4; j++) { -+ if (data->reg_temp[j][i]) -+ data->temp[j][i] -+ = nct6775_read_temp(data, -+ data->reg_temp[j][i]); -+ } -+ if (!(data->have_temp_fixed & (1 << i))) -+ continue; -+ data->temp_offset[i] -+ = nct6775_read_value(data, data->REG_TEMP_OFFSET[i]); -+ } -+ - data->alarms = 0; - for (i = 0; i < NUM_REG_ALARM; i++) { - u8 alarm; -@@ -682,6 +875,275 @@ static const struct attribute_group nct6775_group_in[15] = { - }; - - static ssize_t -+show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]); -+} -+ -+static ssize_t -+show_temp(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); -+ int nr = sattr->nr; -+ int index = sattr->index; -+ -+ return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->temp[index][nr])); -+} -+ -+static ssize_t -+store_temp(struct device *dev, struct device_attribute *attr, const char *buf, -+ size_t count) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); -+ int nr = sattr->nr; -+ int index = sattr->index; -+ int err; -+ long val; -+ -+ err = kstrtol(buf, 10, &val); -+ if (err < 0) -+ return err; -+ -+ mutex_lock(&data->update_lock); -+ data->temp[index][nr] = LM75_TEMP_TO_REG(val); -+ nct6775_write_temp(data, data->reg_temp[index][nr], -+ data->temp[index][nr]); -+ mutex_unlock(&data->update_lock); -+ return count; -+} -+ -+static ssize_t -+show_temp_offset(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ -+ return sprintf(buf, "%d\n", data->temp_offset[sattr->index] * 1000); -+} -+ -+static ssize_t -+store_temp_offset(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ long val; -+ int err; -+ -+ err = kstrtol(buf, 10, &val); -+ if (err < 0) -+ return err; -+ -+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); -+ -+ mutex_lock(&data->update_lock); -+ data->temp_offset[nr] = val; -+ nct6775_write_value(data, data->REG_TEMP_OFFSET[nr], val); -+ mutex_unlock(&data->update_lock); -+ -+ return count; -+} -+ -+static ssize_t -+show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ return sprintf(buf, "%d\n", (int)data->temp_type[nr]); -+} -+ -+static ssize_t -+store_temp_type(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ unsigned long val; -+ int err; -+ u8 vbat, diode, bit; -+ -+ err = kstrtoul(buf, 10, &val); -+ if (err < 0) -+ return err; -+ -+ if (val != 1 && val != 3 && val != 4) -+ return -EINVAL; -+ -+ mutex_lock(&data->update_lock); -+ -+ data->temp_type[nr] = val; -+ vbat = nct6775_read_value(data, data->REG_VBAT) & ~(0x02 << nr); -+ diode = nct6775_read_value(data, data->REG_DIODE) & ~(0x02 << nr); -+ bit = 0x02 << nr; -+ switch (val) { -+ case 1: /* CPU diode (diode, current mode) */ -+ vbat |= bit; -+ diode |= bit; -+ break; -+ case 3: /* diode, voltage mode */ -+ vbat |= bit; -+ break; -+ case 4: /* thermistor */ -+ break; -+ } -+ nct6775_write_value(data, data->REG_VBAT, vbat); -+ nct6775_write_value(data, data->REG_DIODE, diode); -+ -+ mutex_unlock(&data->update_lock); -+ return count; -+} -+ -+static struct sensor_device_attribute_2 sda_temp_input[] = { -+ SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0), -+ SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 1, 0), -+ SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 2, 0), -+ SENSOR_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 3, 0), -+ SENSOR_ATTR_2(temp5_input, S_IRUGO, show_temp, NULL, 4, 0), -+ SENSOR_ATTR_2(temp6_input, S_IRUGO, show_temp, NULL, 5, 0), -+ SENSOR_ATTR_2(temp7_input, S_IRUGO, show_temp, NULL, 6, 0), -+ SENSOR_ATTR_2(temp8_input, S_IRUGO, show_temp, NULL, 7, 0), -+ SENSOR_ATTR_2(temp9_input, S_IRUGO, show_temp, NULL, 8, 0), -+ SENSOR_ATTR_2(temp10_input, S_IRUGO, show_temp, NULL, 9, 0), -+}; -+ -+static struct sensor_device_attribute sda_temp_label[] = { -+ SENSOR_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 0), -+ SENSOR_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 1), -+ SENSOR_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 2), -+ SENSOR_ATTR(temp4_label, S_IRUGO, show_temp_label, NULL, 3), -+ SENSOR_ATTR(temp5_label, S_IRUGO, show_temp_label, NULL, 4), -+ SENSOR_ATTR(temp6_label, S_IRUGO, show_temp_label, NULL, 5), -+ SENSOR_ATTR(temp7_label, S_IRUGO, show_temp_label, NULL, 6), -+ SENSOR_ATTR(temp8_label, S_IRUGO, show_temp_label, NULL, 7), -+ SENSOR_ATTR(temp9_label, S_IRUGO, show_temp_label, NULL, 8), -+ SENSOR_ATTR(temp10_label, S_IRUGO, show_temp_label, NULL, 9), -+}; -+ -+static struct sensor_device_attribute_2 sda_temp_max[] = { -+ SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 0, 1), -+ SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 1, 1), -+ SENSOR_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 2, 1), -+ SENSOR_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 3, 1), -+ SENSOR_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 4, 1), -+ SENSOR_ATTR_2(temp6_max, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 5, 1), -+ SENSOR_ATTR_2(temp7_max, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 6, 1), -+ SENSOR_ATTR_2(temp8_max, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 7, 1), -+ SENSOR_ATTR_2(temp9_max, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 8, 1), -+ SENSOR_ATTR_2(temp10_max, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 9, 1), -+}; -+ -+static struct sensor_device_attribute_2 sda_temp_max_hyst[] = { -+ SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 0, 2), -+ SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 1, 2), -+ SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 2, 2), -+ SENSOR_ATTR_2(temp4_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 3, 2), -+ SENSOR_ATTR_2(temp5_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 4, 2), -+ SENSOR_ATTR_2(temp6_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 5, 2), -+ SENSOR_ATTR_2(temp7_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 6, 2), -+ SENSOR_ATTR_2(temp8_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 7, 2), -+ SENSOR_ATTR_2(temp9_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 8, 2), -+ SENSOR_ATTR_2(temp10_max_hyst, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 9, 2), -+}; -+ -+static struct sensor_device_attribute_2 sda_temp_crit[] = { -+ SENSOR_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 0, 3), -+ SENSOR_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 1, 3), -+ SENSOR_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 2, 3), -+ SENSOR_ATTR_2(temp4_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 3, 3), -+ SENSOR_ATTR_2(temp5_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 4, 3), -+ SENSOR_ATTR_2(temp6_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 5, 3), -+ SENSOR_ATTR_2(temp7_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 6, 3), -+ SENSOR_ATTR_2(temp8_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 7, 3), -+ SENSOR_ATTR_2(temp9_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 8, 3), -+ SENSOR_ATTR_2(temp10_crit, S_IRUGO | S_IWUSR, show_temp, store_temp, -+ 9, 3), -+}; -+ -+static struct sensor_device_attribute sda_temp_offset[] = { -+ SENSOR_ATTR(temp1_offset, S_IRUGO | S_IWUSR, show_temp_offset, -+ store_temp_offset, 0), -+ SENSOR_ATTR(temp2_offset, S_IRUGO | S_IWUSR, show_temp_offset, -+ store_temp_offset, 1), -+ SENSOR_ATTR(temp3_offset, S_IRUGO | S_IWUSR, show_temp_offset, -+ store_temp_offset, 2), -+ SENSOR_ATTR(temp4_offset, S_IRUGO | S_IWUSR, show_temp_offset, -+ store_temp_offset, 3), -+ SENSOR_ATTR(temp5_offset, S_IRUGO | S_IWUSR, show_temp_offset, -+ store_temp_offset, 4), -+ SENSOR_ATTR(temp6_offset, S_IRUGO | S_IWUSR, show_temp_offset, -+ store_temp_offset, 5), -+}; -+ -+static struct sensor_device_attribute sda_temp_type[] = { -+ SENSOR_ATTR(temp1_type, S_IRUGO | S_IWUSR, show_temp_type, -+ store_temp_type, 0), -+ SENSOR_ATTR(temp2_type, S_IRUGO | S_IWUSR, show_temp_type, -+ store_temp_type, 1), -+ SENSOR_ATTR(temp3_type, S_IRUGO | S_IWUSR, show_temp_type, -+ store_temp_type, 2), -+ SENSOR_ATTR(temp4_type, S_IRUGO | S_IWUSR, show_temp_type, -+ store_temp_type, 3), -+ SENSOR_ATTR(temp5_type, S_IRUGO | S_IWUSR, show_temp_type, -+ store_temp_type, 4), -+ SENSOR_ATTR(temp6_type, S_IRUGO | S_IWUSR, show_temp_type, -+ store_temp_type, 5), -+}; -+ -+static struct sensor_device_attribute sda_temp_alarm[] = { -+ SENSOR_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, -+ TEMP_ALARM_BASE), -+ SENSOR_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, -+ TEMP_ALARM_BASE + 1), -+ SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, -+ TEMP_ALARM_BASE + 2), -+ SENSOR_ATTR(temp4_alarm, S_IRUGO, show_alarm, NULL, -+ TEMP_ALARM_BASE + 3), -+ SENSOR_ATTR(temp5_alarm, S_IRUGO, show_alarm, NULL, -+ TEMP_ALARM_BASE + 4), -+ SENSOR_ATTR(temp6_alarm, S_IRUGO, show_alarm, NULL, -+ TEMP_ALARM_BASE + 5), -+}; -+ -+#define NUM_TEMP_ALARM ARRAY_SIZE(sda_temp_alarm) -+ -+static ssize_t - show_name(struct device *dev, struct device_attribute *attr, char *buf) - { - struct nct6775_data *data = dev_get_drvdata(dev); -@@ -766,6 +1228,23 @@ static void nct6775_device_remove_files(struct device *dev) - for (i = 0; i < data->in_num; i++) - sysfs_remove_group(&dev->kobj, &nct6775_group_in[i]); - -+ for (i = 0; i < NUM_TEMP; i++) { -+ if (!(data->have_temp & (1 << i))) -+ continue; -+ device_remove_file(dev, &sda_temp_input[i].dev_attr); -+ device_remove_file(dev, &sda_temp_label[i].dev_attr); -+ device_remove_file(dev, &sda_temp_max[i].dev_attr); -+ device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr); -+ device_remove_file(dev, &sda_temp_crit[i].dev_attr); -+ if (!(data->have_temp_fixed & (1 << i))) -+ continue; -+ device_remove_file(dev, &sda_temp_type[i].dev_attr); -+ device_remove_file(dev, &sda_temp_offset[i].dev_attr); -+ if (i >= NUM_TEMP_ALARM) -+ continue; -+ device_remove_file(dev, &sda_temp_alarm[i].dev_attr); -+ } -+ - device_remove_file(dev, &sda_caseopen[0].dev_attr); - device_remove_file(dev, &sda_caseopen[1].dev_attr); - -@@ -776,7 +1255,8 @@ static void nct6775_device_remove_files(struct device *dev) - /* Get the monitoring functions started */ - static inline void nct6775_init_device(struct nct6775_data *data) - { -- u8 tmp; -+ int i; -+ u8 tmp, diode; - - /* Start monitoring if needed */ - if (data->REG_CONFIG) { -@@ -785,10 +1265,33 @@ static inline void nct6775_init_device(struct nct6775_data *data) - nct6775_write_value(data, data->REG_CONFIG, tmp | 0x01); - } - -+ /* Enable temperature sensors if needed */ -+ for (i = 0; i < NUM_TEMP; i++) { -+ if (!(data->have_temp & (1 << i))) -+ continue; -+ if (!data->reg_temp_config[i]) -+ continue; -+ tmp = nct6775_read_value(data, data->reg_temp_config[i]); -+ if (tmp & 0x01) -+ nct6775_write_value(data, data->reg_temp_config[i], -+ tmp & 0xfe); -+ } -+ - /* Enable VBAT monitoring if needed */ - tmp = nct6775_read_value(data, data->REG_VBAT); - if (!(tmp & 0x01)) - nct6775_write_value(data, data->REG_VBAT, tmp | 0x01); -+ -+ diode = nct6775_read_value(data, data->REG_DIODE); -+ -+ for (i = 0; i < data->temp_fixed_num; i++) { -+ if (!(data->have_temp_fixed & (1 << i))) -+ continue; -+ if ((tmp & (0x02 << i))) /* diode */ -+ data->temp_type[i] = 3 - ((diode >> i) & 0x02); -+ else /* thermistor */ -+ data->temp_type[i] = 4; -+ } - } - - static int nct6775_probe(struct platform_device *pdev) -@@ -797,7 +1300,11 @@ static int nct6775_probe(struct platform_device *pdev) - struct nct6775_sio_data *sio_data = dev->platform_data; - struct nct6775_data *data; - struct resource *res; -- int i, err = 0; -+ int i, s, err = 0; -+ int src, mask, available; -+ const u16 *reg_temp, *reg_temp_over, *reg_temp_hyst, *reg_temp_config; -+ const u16 *reg_temp_alternate, *reg_temp_crit; -+ int num_reg_temp; - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH, -@@ -820,44 +1327,233 @@ static int nct6775_probe(struct platform_device *pdev) - switch (data->kind) { - case nct6775: - data->in_num = 9; -+ data->temp_fixed_num = 3; - - data->ALARM_BITS = NCT6775_ALARM_BITS; - -+ data->temp_label = nct6775_temp_label; -+ data->temp_label_num = ARRAY_SIZE(nct6775_temp_label); -+ - data->REG_CONFIG = NCT6775_REG_CONFIG; - data->REG_VBAT = NCT6775_REG_VBAT; -+ data->REG_DIODE = NCT6775_REG_DIODE; - data->REG_VIN = NCT6775_REG_IN; - data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; - data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; -+ data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; -+ data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; - data->REG_ALARM = NCT6775_REG_ALARM; -+ -+ reg_temp = NCT6775_REG_TEMP; -+ num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP); -+ reg_temp_over = NCT6775_REG_TEMP_OVER; -+ reg_temp_hyst = NCT6775_REG_TEMP_HYST; -+ reg_temp_config = NCT6775_REG_TEMP_CONFIG; -+ reg_temp_alternate = NCT6775_REG_TEMP_ALTERNATE; -+ reg_temp_crit = NCT6775_REG_TEMP_CRIT; -+ - break; - case nct6776: - data->in_num = 9; -+ data->temp_fixed_num = 3; - - data->ALARM_BITS = NCT6776_ALARM_BITS; - -+ data->temp_label = nct6776_temp_label; -+ data->temp_label_num = ARRAY_SIZE(nct6776_temp_label); -+ - data->REG_CONFIG = NCT6775_REG_CONFIG; - data->REG_VBAT = NCT6775_REG_VBAT; -+ data->REG_DIODE = NCT6775_REG_DIODE; - data->REG_VIN = NCT6775_REG_IN; - data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; - data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; -+ data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; -+ data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; - data->REG_ALARM = NCT6775_REG_ALARM; -+ -+ reg_temp = NCT6775_REG_TEMP; -+ num_reg_temp = ARRAY_SIZE(NCT6775_REG_TEMP); -+ reg_temp_over = NCT6775_REG_TEMP_OVER; -+ reg_temp_hyst = NCT6775_REG_TEMP_HYST; -+ reg_temp_config = NCT6776_REG_TEMP_CONFIG; -+ reg_temp_alternate = NCT6776_REG_TEMP_ALTERNATE; -+ reg_temp_crit = NCT6776_REG_TEMP_CRIT; -+ - break; - case nct6779: - data->in_num = 15; -+ data->temp_fixed_num = 6; - - data->ALARM_BITS = NCT6779_ALARM_BITS; - -+ data->temp_label = nct6779_temp_label; -+ data->temp_label_num = ARRAY_SIZE(nct6779_temp_label); -+ - data->REG_CONFIG = NCT6775_REG_CONFIG; - data->REG_VBAT = NCT6775_REG_VBAT; -+ data->REG_DIODE = NCT6775_REG_DIODE; - data->REG_VIN = NCT6779_REG_IN; - data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; - data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; -+ data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET; -+ data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; - data->REG_ALARM = NCT6779_REG_ALARM; -+ -+ reg_temp = NCT6779_REG_TEMP; -+ num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP); -+ reg_temp_over = NCT6779_REG_TEMP_OVER; -+ reg_temp_hyst = NCT6779_REG_TEMP_HYST; -+ reg_temp_config = NCT6779_REG_TEMP_CONFIG; -+ reg_temp_alternate = NCT6779_REG_TEMP_ALTERNATE; -+ reg_temp_crit = NCT6779_REG_TEMP_CRIT; -+ - break; - default: - return -ENODEV; - } - data->have_in = (1 << data->in_num) - 1; -+ data->have_temp = 0; -+ -+ /* -+ * On some boards, not all available temperature sources are monitored, -+ * even though some of the monitoring registers are unused. -+ * Get list of unused monitoring registers, then detect if any fan -+ * controls are configured to use unmonitored temperature sources. -+ * If so, assign the unmonitored temperature sources to available -+ * monitoring registers. -+ */ -+ mask = 0; -+ available = 0; -+ for (i = 0; i < num_reg_temp; i++) { -+ if (reg_temp[i] == 0) -+ continue; -+ -+ src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f; -+ if (!src || (mask & (1 << src))) -+ available |= 1 << i; -+ -+ mask |= 1 << src; -+ } -+ -+ mask = 0; -+ s = NUM_TEMP_FIXED; /* First dynamic temperature attribute */ -+ for (i = 0; i < num_reg_temp; i++) { -+ if (reg_temp[i] == 0) -+ continue; -+ -+ src = nct6775_read_value(data, data->REG_TEMP_SOURCE[i]) & 0x1f; -+ if (!src || (mask & (1 << src))) -+ continue; -+ -+ if (src >= data->temp_label_num || -+ !strlen(data->temp_label[src])) { -+ dev_info(dev, -+ "Invalid temperature source %d at index %d, source register 0x%x, temp register 0x%x\n", -+ src, i, data->REG_TEMP_SOURCE[i], reg_temp[i]); -+ continue; -+ } -+ -+ mask |= 1 << src; -+ -+ /* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */ -+ if (src <= data->temp_fixed_num) { -+ data->have_temp |= 1 << (src - 1); -+ data->have_temp_fixed |= 1 << (src - 1); -+ data->reg_temp[0][src - 1] = reg_temp[i]; -+ data->reg_temp[1][src - 1] = reg_temp_over[i]; -+ data->reg_temp[2][src - 1] = reg_temp_hyst[i]; -+ data->reg_temp_config[src - 1] = reg_temp_config[i]; -+ data->temp_src[src - 1] = src; -+ continue; -+ } -+ -+ if (s >= NUM_TEMP) -+ continue; -+ -+ /* Use dynamic index for other sources */ -+ data->have_temp |= 1 << s; -+ data->reg_temp[0][s] = reg_temp[i]; -+ data->reg_temp[1][s] = reg_temp_over[i]; -+ data->reg_temp[2][s] = reg_temp_hyst[i]; -+ data->reg_temp_config[s] = reg_temp_config[i]; -+ if (reg_temp_crit[src - 1]) -+ data->reg_temp[3][s] = reg_temp_crit[src - 1]; -+ -+ data->temp_src[s] = src; -+ s++; -+ } -+ -+#ifdef USE_ALTERNATE -+ /* -+ * Go through the list of alternate temp registers and enable -+ * if possible. -+ * The temperature is already monitored if the respective bit in -+ * is set. -+ */ -+ for (i = 0; i < data->temp_label_num - 1; i++) { -+ if (!reg_temp_alternate[i]) -+ continue; -+ if (mask & (1 << (i + 1))) -+ continue; -+ if (i < data->temp_fixed_num) { -+ if (data->have_temp & (1 << i)) -+ continue; -+ data->have_temp |= 1 << i; -+ data->have_temp_fixed |= 1 << i; -+ data->reg_temp[0][i] = reg_temp_alternate[i]; -+ data->reg_temp[1][i] = reg_temp_over[i]; -+ data->reg_temp[2][i] = reg_temp_hyst[i]; -+ data->temp_src[i] = i + 1; -+ continue; -+ } -+ -+ if (s >= NUM_TEMP) /* Abort if no more space */ -+ break; -+ -+ data->have_temp |= 1 << s; -+ data->reg_temp[0][s] = reg_temp_alternate[i]; -+ data->temp_src[s] = i + 1; -+ s++; -+ } -+#endif /* USE_ALTERNATE */ -+ -+ switch (data->kind) { -+ case nct6775: -+ break; -+ case nct6776: -+ /* -+ * On NCT6776, AUXTIN and VIN3 pins are shared. -+ * Only way to detect it is to check if AUXTIN is used -+ * as a temperature source, and if that source is -+ * enabled. -+ * -+ * If that is the case, disable in6, which reports VIN3. -+ * Otherwise disable temp3. -+ */ -+ if (data->have_temp & (1 << 2)) { -+ u8 reg = nct6775_read_value(data, -+ data->reg_temp_config[2]); -+ if (reg & 0x01) -+ data->have_temp &= ~(1 << 2); -+ else -+ data->have_in &= ~(1 << 6); -+ } -+ break; -+ case nct6779: -+ /* -+ * Shared pins: -+ * VIN4 / AUXTIN0 -+ * VIN5 / AUXTIN1 -+ * VIN6 / AUXTIN2 -+ * VIN7 / AUXTIN3 -+ * -+ * There does not seem to be a clean way to detect if VINx or -+ * AUXTINx is active, so for keep both sensor types enabled -+ * for now. -+ */ -+ break; -+ } - - /* Initialize the chip */ - nct6775_init_device(data); -@@ -887,6 +1583,52 @@ static int nct6775_probe(struct platform_device *pdev) - goto exit_remove; - } - -+ for (i = 0; i < NUM_TEMP; i++) { -+ if (!(data->have_temp & (1 << i))) -+ continue; -+ err = device_create_file(dev, &sda_temp_input[i].dev_attr); -+ if (err) -+ goto exit_remove; -+ if (data->temp_label) { -+ err = device_create_file(dev, -+ &sda_temp_label[i].dev_attr); -+ if (err) -+ goto exit_remove; -+ } -+ if (data->reg_temp[1][i]) { -+ err = device_create_file(dev, -+ &sda_temp_max[i].dev_attr); -+ if (err) -+ goto exit_remove; -+ } -+ if (data->reg_temp[2][i]) { -+ err = device_create_file(dev, -+ &sda_temp_max_hyst[i].dev_attr); -+ if (err) -+ goto exit_remove; -+ } -+ if (data->reg_temp[3][i]) { -+ err = device_create_file(dev, -+ &sda_temp_crit[i].dev_attr); -+ if (err) -+ goto exit_remove; -+ } -+ if (!(data->have_temp_fixed & (1 << i))) -+ continue; -+ err = device_create_file(dev, &sda_temp_type[i].dev_attr); -+ if (err) -+ goto exit_remove; -+ err = device_create_file(dev, &sda_temp_offset[i].dev_attr); -+ if (err) -+ goto exit_remove; -+ if (i >= NUM_TEMP_ALARM || -+ data->ALARM_BITS[TEMP_ALARM_BASE + i] < 0) -+ continue; -+ err = device_create_file(dev, &sda_temp_alarm[i].dev_attr); -+ if (err) -+ goto exit_remove; -+ } -+ - for (i = 0; i < ARRAY_SIZE(sda_caseopen); i++) { - if (data->ALARM_BITS[INTRUSION_ALARM_BASE + i] < 0) - continue; - -From 1c65dc365ed38d6839fcc231ea38a6163fb9d343 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Tue, 4 Dec 2012 07:56:24 -0800 -Subject: [PATCH] hwmon: (nct6775) Add support for fan speed attributes - -Signed-off-by: Guenter Roeck - -diff --git a/Documentation/hwmon/nct6775 b/Documentation/hwmon/nct6775 -index ccfd5cc..dcd56a3 100644 ---- a/Documentation/hwmon/nct6775 -+++ b/Documentation/hwmon/nct6775 -@@ -53,8 +53,9 @@ triggered if the rotation speed has dropped below a programmable limit. On - NCT6775F, fan readings can be divided by a programmable divider (1, 2, 4, 8, - 16, 32, 64 or 128) to give the readings more range or accuracy; the other chips - do not have a fan speed divider. The driver sets the most suitable fan divisor --itself; specifically, it doubles the divider value each time a fan speed reading --returns an invalid value. Some fans might not be present because they share pins -+itself; specifically, it increases the divider value each time a fan speed -+reading returns an invalid value, and it reduces it if the fan speed reading -+is lower than optimal. Some fans might not be present because they share pins - with other functions. - - Voltage sensors (also known as IN sensors) report their values in millivolts. -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index fd0dd15..bafcae5 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -180,6 +180,9 @@ static const u16 NCT6775_REG_IN[] = { - #define NCT6775_REG_VBAT 0x5D - #define NCT6775_REG_DIODE 0x5E - -+#define NCT6775_REG_FANDIV1 0x506 -+#define NCT6775_REG_FANDIV2 0x507 -+ - static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B }; - - /* 0..15 voltages, 16..23 fans, 24..31 temperatures */ -@@ -193,12 +196,16 @@ static const s8 NCT6775_ALARM_BITS[] = { - 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ - 12, -1 }; /* intrusion0, intrusion1 */ - -+#define FAN_ALARM_BASE 16 - #define TEMP_ALARM_BASE 24 - #define INTRUSION_ALARM_BASE 30 - - static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee }; - static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 }; - -+static const u16 NCT6775_REG_FAN[] = { 0x630, 0x632, 0x634, 0x636, 0x638 }; -+static const u16 NCT6775_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d }; -+ - static const u16 NCT6775_REG_TEMP[] = { - 0x27, 0x150, 0x250, 0x62b, 0x62c, 0x62d }; - -@@ -256,6 +263,8 @@ static const s8 NCT6776_ALARM_BITS[] = { - 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ - 12, 9 }; /* intrusion0, intrusion1 */ - -+static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642 }; -+ - static const u16 NCT6776_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = { - 0x18, 0x152, 0x252, 0x628, 0x629, 0x62A }; - -@@ -309,6 +318,8 @@ static const s8 NCT6779_ALARM_BITS[] = { - 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ - 12, 9 }; /* intrusion0, intrusion1 */ - -+static const u16 NCT6779_REG_FAN[] = { 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8 }; -+ - static const u16 NCT6779_REG_TEMP[] = { 0x27, 0x150 }; - static const u16 NCT6779_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6779_REG_TEMP)] = { - 0x18, 0x152 }; -@@ -363,6 +374,44 @@ static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1] - * Conversions - */ - -+static unsigned int fan_from_reg8(u16 reg, unsigned int divreg) -+{ -+ if (reg == 0 || reg == 255) -+ return 0; -+ return 1350000U / (reg << divreg); -+} -+ -+static unsigned int fan_from_reg13(u16 reg, unsigned int divreg) -+{ -+ if ((reg & 0xff1f) == 0xff1f) -+ return 0; -+ -+ reg = (reg & 0x1f) | ((reg & 0xff00) >> 3); -+ -+ if (reg == 0) -+ return 0; -+ -+ return 1350000U / reg; -+} -+ -+static unsigned int fan_from_reg16(u16 reg, unsigned int divreg) -+{ -+ if (reg == 0 || reg == 0xffff) -+ return 0; -+ -+ /* -+ * Even though the registers are 16 bit wide, the fan divisor -+ * still applies. -+ */ -+ return 1350000U / (reg << divreg); -+} -+ -+static inline unsigned int -+div_from_reg(u8 reg) -+{ -+ return 1 << reg; -+} -+ - /* - * Some of the voltage inputs have internal scaling, the tables below - * contain 8 (the ADC LSB in mV) * scaling factor * 100 -@@ -411,12 +460,17 @@ struct nct6775_data { - const u16 *REG_VIN; - const u16 *REG_IN_MINMAX[2]; - -- const u16 *REG_TEMP_SOURCE; /* temp register sources */ -+ const u16 *REG_FAN; -+ const u16 *REG_FAN_MIN; - -+ const u16 *REG_TEMP_SOURCE; /* temp register sources */ - const u16 *REG_TEMP_OFFSET; - - const u16 *REG_ALARM; - -+ unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg); -+ unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg); -+ - struct mutex update_lock; - bool valid; /* true if following fields are valid */ - unsigned long last_updated; /* In jiffies */ -@@ -425,6 +479,12 @@ struct nct6775_data { - u8 bank; /* current register bank */ - u8 in_num; /* number of in inputs we have */ - u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */ -+ unsigned int rpm[5]; -+ u16 fan_min[5]; -+ u8 fan_div[5]; -+ u8 has_fan; /* some fan inputs can be disabled */ -+ u8 has_fan_min; /* some fans don't have min register */ -+ bool has_fan_div; - - u8 temp_fixed_num; /* 3 or 6 */ - u8 temp_type[NUM_TEMP_FIXED]; -@@ -556,6 +616,153 @@ static int nct6775_write_temp(struct nct6775_data *data, u16 reg, u16 value) - return nct6775_write_value(data, reg, value); - } - -+/* This function assumes that the caller holds data->update_lock */ -+static void nct6775_write_fan_div(struct nct6775_data *data, int nr) -+{ -+ u8 reg; -+ -+ switch (nr) { -+ case 0: -+ reg = (nct6775_read_value(data, NCT6775_REG_FANDIV1) & 0x70) -+ | (data->fan_div[0] & 0x7); -+ nct6775_write_value(data, NCT6775_REG_FANDIV1, reg); -+ break; -+ case 1: -+ reg = (nct6775_read_value(data, NCT6775_REG_FANDIV1) & 0x7) -+ | ((data->fan_div[1] << 4) & 0x70); -+ nct6775_write_value(data, NCT6775_REG_FANDIV1, reg); -+ break; -+ case 2: -+ reg = (nct6775_read_value(data, NCT6775_REG_FANDIV2) & 0x70) -+ | (data->fan_div[2] & 0x7); -+ nct6775_write_value(data, NCT6775_REG_FANDIV2, reg); -+ break; -+ case 3: -+ reg = (nct6775_read_value(data, NCT6775_REG_FANDIV2) & 0x7) -+ | ((data->fan_div[3] << 4) & 0x70); -+ nct6775_write_value(data, NCT6775_REG_FANDIV2, reg); -+ break; -+ } -+} -+ -+static void nct6775_write_fan_div_common(struct nct6775_data *data, int nr) -+{ -+ if (data->kind == nct6775) -+ nct6775_write_fan_div(data, nr); -+} -+ -+static void nct6775_update_fan_div(struct nct6775_data *data) -+{ -+ u8 i; -+ -+ i = nct6775_read_value(data, NCT6775_REG_FANDIV1); -+ data->fan_div[0] = i & 0x7; -+ data->fan_div[1] = (i & 0x70) >> 4; -+ i = nct6775_read_value(data, NCT6775_REG_FANDIV2); -+ data->fan_div[2] = i & 0x7; -+ if (data->has_fan & (1<<3)) -+ data->fan_div[3] = (i & 0x70) >> 4; -+} -+ -+static void nct6775_update_fan_div_common(struct nct6775_data *data) -+{ -+ if (data->kind == nct6775) -+ nct6775_update_fan_div(data); -+} -+ -+static void nct6775_init_fan_div(struct nct6775_data *data) -+{ -+ int i; -+ -+ nct6775_update_fan_div_common(data); -+ /* -+ * For all fans, start with highest divider value if the divider -+ * register is not initialized. This ensures that we get a -+ * reading from the fan count register, even if it is not optimal. -+ * We'll compute a better divider later on. -+ */ -+ for (i = 0; i < 3; i++) { -+ if (!(data->has_fan & (1 << i))) -+ continue; -+ if (data->fan_div[i] == 0) { -+ data->fan_div[i] = 7; -+ nct6775_write_fan_div_common(data, i); -+ } -+ } -+} -+ -+static void nct6775_init_fan_common(struct device *dev, -+ struct nct6775_data *data) -+{ -+ int i; -+ u8 reg; -+ -+ if (data->has_fan_div) -+ nct6775_init_fan_div(data); -+ -+ /* -+ * If fan_min is not set (0), set it to 0xff to disable it. This -+ * prevents the unnecessary warning when fanX_min is reported as 0. -+ */ -+ for (i = 0; i < 5; i++) { -+ if (data->has_fan_min & (1 << i)) { -+ reg = nct6775_read_value(data, data->REG_FAN_MIN[i]); -+ if (!reg) -+ nct6775_write_value(data, data->REG_FAN_MIN[i], -+ data->has_fan_div ? 0xff -+ : 0xff1f); -+ } -+ } -+} -+ -+static void nct6775_select_fan_div(struct device *dev, -+ struct nct6775_data *data, int nr, u16 reg) -+{ -+ u8 fan_div = data->fan_div[nr]; -+ u16 fan_min; -+ -+ if (!data->has_fan_div) -+ return; -+ -+ /* -+ * If we failed to measure the fan speed, or the reported value is not -+ * in the optimal range, and the clock divider can be modified, -+ * let's try that for next time. -+ */ -+ if (reg == 0x00 && fan_div < 0x07) -+ fan_div++; -+ else if (reg != 0x00 && reg < 0x30 && fan_div > 0) -+ fan_div--; -+ -+ if (fan_div != data->fan_div[nr]) { -+ dev_dbg(dev, "Modifying fan%d clock divider from %u to %u\n", -+ nr + 1, div_from_reg(data->fan_div[nr]), -+ div_from_reg(fan_div)); -+ -+ /* Preserve min limit if possible */ -+ if (data->has_fan_min & (1 << nr)) { -+ fan_min = data->fan_min[nr]; -+ if (fan_div > data->fan_div[nr]) { -+ if (fan_min != 255 && fan_min > 1) -+ fan_min >>= 1; -+ } else { -+ if (fan_min != 255) { -+ fan_min <<= 1; -+ if (fan_min > 254) -+ fan_min = 254; -+ } -+ } -+ if (fan_min != data->fan_min[nr]) { -+ data->fan_min[nr] = fan_min; -+ nct6775_write_value(data, data->REG_FAN_MIN[nr], -+ fan_min); -+ } -+ } -+ data->fan_div[nr] = fan_div; -+ nct6775_write_fan_div_common(data, nr); -+ } -+} -+ - static struct nct6775_data *nct6775_update_device(struct device *dev) - { - struct nct6775_data *data = dev_get_drvdata(dev); -@@ -565,6 +772,9 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) - - if (time_after(jiffies, data->last_updated + HZ + HZ/2) - || !data->valid) { -+ /* Fan clock dividers */ -+ nct6775_update_fan_div_common(data); -+ - /* Measured voltages and limits */ - for (i = 0; i < data->in_num; i++) { - if (!(data->have_in & (1 << i))) -@@ -578,6 +788,24 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) - data->REG_IN_MINMAX[1][i]); - } - -+ /* Measured fan speeds and limits */ -+ for (i = 0; i < 5; i++) { -+ u16 reg; -+ -+ if (!(data->has_fan & (1 << i))) -+ continue; -+ -+ reg = nct6775_read_value(data, data->REG_FAN[i]); -+ data->rpm[i] = data->fan_from_reg(reg, -+ data->fan_div[i]); -+ -+ if (data->has_fan_min & (1 << i)) -+ data->fan_min[i] = nct6775_read_value(data, -+ data->REG_FAN_MIN[i]); -+ -+ nct6775_select_fan_div(dev, data, i, reg); -+ } -+ - /* Measured temperatures and limits */ - for (i = 0; i < NUM_TEMP; i++) { - if (!(data->have_temp & (1 << i))) -@@ -875,6 +1103,166 @@ static const struct attribute_group nct6775_group_in[15] = { - }; - - static ssize_t -+show_fan(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ return sprintf(buf, "%d\n", data->rpm[nr]); -+} -+ -+static ssize_t -+show_fan_min(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ return sprintf(buf, "%d\n", -+ data->fan_from_reg_min(data->fan_min[nr], -+ data->fan_div[nr])); -+} -+ -+static ssize_t -+show_fan_div(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr])); -+} -+ -+static ssize_t -+store_fan_min(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ unsigned long val; -+ int err; -+ unsigned int reg; -+ u8 new_div; -+ -+ err = kstrtoul(buf, 10, &val); -+ if (err < 0) -+ return err; -+ -+ mutex_lock(&data->update_lock); -+ if (!data->has_fan_div) { -+ /* NCT6776F or NCT6779D; we know this is a 13 bit register */ -+ if (!val) { -+ val = 0xff1f; -+ } else { -+ if (val > 1350000U) -+ val = 135000U; -+ val = 1350000U / val; -+ val = (val & 0x1f) | ((val << 3) & 0xff00); -+ } -+ data->fan_min[nr] = val; -+ goto write_min; /* Leave fan divider alone */ -+ } -+ if (!val) { -+ /* No min limit, alarm disabled */ -+ data->fan_min[nr] = 255; -+ new_div = data->fan_div[nr]; /* No change */ -+ dev_info(dev, "fan%u low limit and alarm disabled\n", nr + 1); -+ goto write_div; -+ } -+ reg = 1350000U / val; -+ if (reg >= 128 * 255) { -+ /* -+ * Speed below this value cannot possibly be represented, -+ * even with the highest divider (128) -+ */ -+ data->fan_min[nr] = 254; -+ new_div = 7; /* 128 == (1 << 7) */ -+ dev_warn(dev, -+ "fan%u low limit %lu below minimum %u, set to minimum\n", -+ nr + 1, val, data->fan_from_reg_min(254, 7)); -+ } else if (!reg) { -+ /* -+ * Speed above this value cannot possibly be represented, -+ * even with the lowest divider (1) -+ */ -+ data->fan_min[nr] = 1; -+ new_div = 0; /* 1 == (1 << 0) */ -+ dev_warn(dev, -+ "fan%u low limit %lu above maximum %u, set to maximum\n", -+ nr + 1, val, data->fan_from_reg_min(1, 0)); -+ } else { -+ /* -+ * Automatically pick the best divider, i.e. the one such -+ * that the min limit will correspond to a register value -+ * in the 96..192 range -+ */ -+ new_div = 0; -+ while (reg > 192 && new_div < 7) { -+ reg >>= 1; -+ new_div++; -+ } -+ data->fan_min[nr] = reg; -+ } -+ -+write_div: -+ /* -+ * Write both the fan clock divider (if it changed) and the new -+ * fan min (unconditionally) -+ */ -+ if (new_div != data->fan_div[nr]) { -+ dev_dbg(dev, "fan%u clock divider changed from %u to %u\n", -+ nr + 1, div_from_reg(data->fan_div[nr]), -+ div_from_reg(new_div)); -+ data->fan_div[nr] = new_div; -+ nct6775_write_fan_div_common(data, nr); -+ /* Give the chip time to sample a new speed value */ -+ data->last_updated = jiffies; -+ } -+ -+write_min: -+ nct6775_write_value(data, data->REG_FAN_MIN[nr], data->fan_min[nr]); -+ mutex_unlock(&data->update_lock); -+ -+ return count; -+} -+ -+static struct sensor_device_attribute sda_fan_input[] = { -+ SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0), -+ SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1), -+ SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2), -+ SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3), -+ SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4), -+}; -+ -+static struct sensor_device_attribute sda_fan_alarm[] = { -+ SENSOR_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE), -+ SENSOR_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 1), -+ SENSOR_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 2), -+ SENSOR_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 3), -+ SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, FAN_ALARM_BASE + 4), -+}; -+ -+static struct sensor_device_attribute sda_fan_min[] = { -+ SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, -+ store_fan_min, 0), -+ SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, -+ store_fan_min, 1), -+ SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, -+ store_fan_min, 2), -+ SENSOR_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min, -+ store_fan_min, 3), -+ SENSOR_ATTR(fan5_min, S_IWUSR | S_IRUGO, show_fan_min, -+ store_fan_min, 4), -+}; -+ -+static struct sensor_device_attribute sda_fan_div[] = { -+ SENSOR_ATTR(fan1_div, S_IRUGO, show_fan_div, NULL, 0), -+ SENSOR_ATTR(fan2_div, S_IRUGO, show_fan_div, NULL, 1), -+ SENSOR_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2), -+ SENSOR_ATTR(fan4_div, S_IRUGO, show_fan_div, NULL, 3), -+ SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4), -+}; -+ -+static ssize_t - show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) - { - struct nct6775_data *data = nct6775_update_device(dev); -@@ -1228,6 +1616,12 @@ static void nct6775_device_remove_files(struct device *dev) - for (i = 0; i < data->in_num; i++) - sysfs_remove_group(&dev->kobj, &nct6775_group_in[i]); - -+ for (i = 0; i < 5; i++) { -+ device_remove_file(dev, &sda_fan_input[i].dev_attr); -+ device_remove_file(dev, &sda_fan_alarm[i].dev_attr); -+ device_remove_file(dev, &sda_fan_div[i].dev_attr); -+ device_remove_file(dev, &sda_fan_min[i].dev_attr); -+ } - for (i = 0; i < NUM_TEMP; i++) { - if (!(data->have_temp & (1 << i))) - continue; -@@ -1294,6 +1688,75 @@ static inline void nct6775_init_device(struct nct6775_data *data) - } - } - -+static int -+nct6775_check_fan_inputs(const struct nct6775_sio_data *sio_data, -+ struct nct6775_data *data) -+{ -+ int regval; -+ bool fan3pin, fan3min, fan4pin, fan4min, fan5pin; -+ int ret; -+ -+ ret = superio_enter(sio_data->sioreg); -+ if (ret) -+ return ret; -+ -+ /* fan4 and fan5 share some pins with the GPIO and serial flash */ -+ if (data->kind == nct6775) { -+ regval = superio_inb(sio_data->sioreg, 0x2c); -+ -+ fan3pin = regval & (1 << 6); -+ fan3min = fan3pin; -+ -+ /* On NCT6775, fan4 shares pins with the fdc interface */ -+ fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80); -+ fan4min = 0; -+ fan5pin = 0; -+ } else if (data->kind == nct6776) { -+ bool gpok = superio_inb(sio_data->sioreg, 0x27) & 0x80; -+ -+ superio_select(sio_data->sioreg, NCT6775_LD_HWM); -+ regval = superio_inb(sio_data->sioreg, SIO_REG_ENABLE); -+ -+ if (regval & 0x80) -+ fan3pin = gpok; -+ else -+ fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40); -+ -+ if (regval & 0x40) -+ fan4pin = gpok; -+ else -+ fan4pin = superio_inb(sio_data->sioreg, 0x1C) & 0x01; -+ -+ if (regval & 0x20) -+ fan5pin = gpok; -+ else -+ fan5pin = superio_inb(sio_data->sioreg, 0x1C) & 0x02; -+ -+ fan4min = fan4pin; -+ fan3min = fan3pin; -+ } else { /* NCT6779D */ -+ regval = superio_inb(sio_data->sioreg, 0x1c); -+ -+ fan3pin = !(regval & (1 << 5)); -+ fan4pin = !(regval & (1 << 6)); -+ fan5pin = !(regval & (1 << 7)); -+ -+ fan3min = fan3pin; -+ fan4min = fan4pin; -+ } -+ -+ superio_exit(sio_data->sioreg); -+ -+ data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */ -+ data->has_fan |= fan3pin << 2; -+ data->has_fan_min |= fan3min << 2; -+ -+ data->has_fan |= (fan4pin << 3) | (fan5pin << 4); -+ data->has_fan_min |= (fan4min << 3) | (fan5pin << 4); -+ -+ return 0; -+} -+ - static int nct6775_probe(struct platform_device *pdev) - { - struct device *dev = &pdev->dev; -@@ -1327,10 +1790,14 @@ static int nct6775_probe(struct platform_device *pdev) - switch (data->kind) { - case nct6775: - data->in_num = 9; -+ data->has_fan_div = true; - data->temp_fixed_num = 3; - - data->ALARM_BITS = NCT6775_ALARM_BITS; - -+ data->fan_from_reg = fan_from_reg16; -+ data->fan_from_reg_min = fan_from_reg8; -+ - data->temp_label = nct6775_temp_label; - data->temp_label_num = ARRAY_SIZE(nct6775_temp_label); - -@@ -1340,6 +1807,8 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_VIN = NCT6775_REG_IN; - data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; - data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; -+ data->REG_FAN = NCT6775_REG_FAN; -+ data->REG_FAN_MIN = NCT6775_REG_FAN_MIN; - data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; - data->REG_ALARM = NCT6775_REG_ALARM; -@@ -1355,10 +1824,14 @@ static int nct6775_probe(struct platform_device *pdev) - break; - case nct6776: - data->in_num = 9; -+ data->has_fan_div = false; - data->temp_fixed_num = 3; - - data->ALARM_BITS = NCT6776_ALARM_BITS; - -+ data->fan_from_reg = fan_from_reg13; -+ data->fan_from_reg_min = fan_from_reg13; -+ - data->temp_label = nct6776_temp_label; - data->temp_label_num = ARRAY_SIZE(nct6776_temp_label); - -@@ -1368,6 +1841,8 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_VIN = NCT6775_REG_IN; - data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; - data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; -+ data->REG_FAN = NCT6775_REG_FAN; -+ data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; - data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; - data->REG_ALARM = NCT6775_REG_ALARM; -@@ -1383,10 +1858,14 @@ static int nct6775_probe(struct platform_device *pdev) - break; - case nct6779: - data->in_num = 15; -+ data->has_fan_div = false; - data->temp_fixed_num = 6; - - data->ALARM_BITS = NCT6779_ALARM_BITS; - -+ data->fan_from_reg = fan_from_reg13; -+ data->fan_from_reg_min = fan_from_reg13; -+ - data->temp_label = nct6779_temp_label; - data->temp_label_num = ARRAY_SIZE(nct6779_temp_label); - -@@ -1396,6 +1875,8 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_VIN = NCT6779_REG_IN; - data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; - data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; -+ data->REG_FAN = NCT6779_REG_FAN; -+ data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; - data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; - data->REG_ALARM = NCT6779_REG_ALARM; -@@ -1575,6 +2056,13 @@ static int nct6775_probe(struct platform_device *pdev) - if (err) - return err; - -+ err = nct6775_check_fan_inputs(sio_data, data); -+ if (err) -+ goto exit_remove; -+ -+ /* Read fan clock dividers immediately */ -+ nct6775_init_fan_common(dev, data); -+ - for (i = 0; i < data->in_num; i++) { - if (!(data->have_in & (1 << i))) - continue; -@@ -1583,6 +2071,32 @@ static int nct6775_probe(struct platform_device *pdev) - goto exit_remove; - } - -+ for (i = 0; i < 5; i++) { -+ if (data->has_fan & (1 << i)) { -+ err = device_create_file(dev, -+ &sda_fan_input[i].dev_attr); -+ if (err) -+ goto exit_remove; -+ err = device_create_file(dev, -+ &sda_fan_alarm[i].dev_attr); -+ if (err) -+ goto exit_remove; -+ if (data->kind != nct6776 && -+ data->kind != nct6779) { -+ err = device_create_file(dev, -+ &sda_fan_div[i].dev_attr); -+ if (err) -+ goto exit_remove; -+ } -+ if (data->has_fan_min & (1 << i)) { -+ err = device_create_file(dev, -+ &sda_fan_min[i].dev_attr); -+ if (err) -+ goto exit_remove; -+ } -+ } -+ } -+ - for (i = 0; i < NUM_TEMP; i++) { - if (!(data->have_temp & (1 << i))) - continue; - -From 5c25d954d37b7c18606d7ef99122424552b86ef2 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Tue, 11 Dec 2012 07:29:06 -0800 -Subject: [PATCH] hwmon: (nct6775) Add support for fanX_pulses sysfs attribute - -Signed-off-by: Guenter Roeck - -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index bafcae5..fea6ed7 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -205,6 +205,7 @@ static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 }; - - static const u16 NCT6775_REG_FAN[] = { 0x630, 0x632, 0x634, 0x636, 0x638 }; - static const u16 NCT6775_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d }; -+static const u16 NCT6775_REG_FAN_PULSES[] = { 0x641, 0x642, 0x643, 0x644, 0 }; - - static const u16 NCT6775_REG_TEMP[] = { - 0x27, 0x150, 0x250, 0x62b, 0x62c, 0x62d }; -@@ -264,6 +265,7 @@ static const s8 NCT6776_ALARM_BITS[] = { - 12, 9 }; /* intrusion0, intrusion1 */ - - static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642 }; -+static const u16 NCT6776_REG_FAN_PULSES[] = { 0x644, 0x645, 0x646, 0, 0 }; - - static const u16 NCT6776_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = { - 0x18, 0x152, 0x252, 0x628, 0x629, 0x62A }; -@@ -319,6 +321,8 @@ static const s8 NCT6779_ALARM_BITS[] = { - 12, 9 }; /* intrusion0, intrusion1 */ - - static const u16 NCT6779_REG_FAN[] = { 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8 }; -+static const u16 NCT6779_REG_FAN_PULSES[] = { -+ 0x644, 0x645, 0x646, 0x647, 0x648 }; - - static const u16 NCT6779_REG_TEMP[] = { 0x27, 0x150 }; - static const u16 NCT6779_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6779_REG_TEMP)] = { -@@ -462,6 +466,7 @@ struct nct6775_data { - - const u16 *REG_FAN; - const u16 *REG_FAN_MIN; -+ const u16 *REG_FAN_PULSES; - - const u16 *REG_TEMP_SOURCE; /* temp register sources */ - const u16 *REG_TEMP_OFFSET; -@@ -481,6 +486,7 @@ struct nct6775_data { - u8 in[15][3]; /* [0]=in, [1]=in_max, [2]=in_min */ - unsigned int rpm[5]; - u16 fan_min[5]; -+ u8 fan_pulses[5]; - u8 fan_div[5]; - u8 has_fan; /* some fan inputs can be disabled */ - u8 has_fan_min; /* some fans don't have min register */ -@@ -802,6 +808,8 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) - if (data->has_fan_min & (1 << i)) - data->fan_min[i] = nct6775_read_value(data, - data->REG_FAN_MIN[i]); -+ data->fan_pulses[i] = -+ nct6775_read_value(data, data->REG_FAN_PULSES[i]); - - nct6775_select_fan_div(dev, data, i, reg); - } -@@ -1225,6 +1233,41 @@ write_min: - return count; - } - -+static ssize_t -+show_fan_pulses(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int p = data->fan_pulses[sattr->index]; -+ -+ return sprintf(buf, "%d\n", p ? : 4); -+} -+ -+static ssize_t -+store_fan_pulses(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ unsigned long val; -+ int err; -+ -+ err = kstrtoul(buf, 10, &val); -+ if (err < 0) -+ return err; -+ -+ if (val > 4) -+ return -EINVAL; -+ -+ mutex_lock(&data->update_lock); -+ data->fan_pulses[nr] = val & 3; -+ nct6775_write_value(data, data->REG_FAN_PULSES[nr], val & 3); -+ mutex_unlock(&data->update_lock); -+ -+ return count; -+} -+ - static struct sensor_device_attribute sda_fan_input[] = { - SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0), - SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1), -@@ -1254,6 +1297,19 @@ static struct sensor_device_attribute sda_fan_min[] = { - store_fan_min, 4), - }; - -+static struct sensor_device_attribute sda_fan_pulses[] = { -+ SENSOR_ATTR(fan1_pulses, S_IWUSR | S_IRUGO, show_fan_pulses, -+ store_fan_pulses, 0), -+ SENSOR_ATTR(fan2_pulses, S_IWUSR | S_IRUGO, show_fan_pulses, -+ store_fan_pulses, 1), -+ SENSOR_ATTR(fan3_pulses, S_IWUSR | S_IRUGO, show_fan_pulses, -+ store_fan_pulses, 2), -+ SENSOR_ATTR(fan4_pulses, S_IWUSR | S_IRUGO, show_fan_pulses, -+ store_fan_pulses, 3), -+ SENSOR_ATTR(fan5_pulses, S_IWUSR | S_IRUGO, show_fan_pulses, -+ store_fan_pulses, 4), -+}; -+ - static struct sensor_device_attribute sda_fan_div[] = { - SENSOR_ATTR(fan1_div, S_IRUGO, show_fan_div, NULL, 0), - SENSOR_ATTR(fan2_div, S_IRUGO, show_fan_div, NULL, 1), -@@ -1621,6 +1677,7 @@ static void nct6775_device_remove_files(struct device *dev) - device_remove_file(dev, &sda_fan_alarm[i].dev_attr); - device_remove_file(dev, &sda_fan_div[i].dev_attr); - device_remove_file(dev, &sda_fan_min[i].dev_attr); -+ device_remove_file(dev, &sda_fan_pulses[i].dev_attr); - } - for (i = 0; i < NUM_TEMP; i++) { - if (!(data->have_temp & (1 << i))) -@@ -1809,6 +1866,7 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; - data->REG_FAN = NCT6775_REG_FAN; - data->REG_FAN_MIN = NCT6775_REG_FAN_MIN; -+ data->REG_FAN_PULSES = NCT6775_REG_FAN_PULSES; - data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; - data->REG_ALARM = NCT6775_REG_ALARM; -@@ -1843,6 +1901,7 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; - data->REG_FAN = NCT6775_REG_FAN; - data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; -+ data->REG_FAN_PULSES = NCT6776_REG_FAN_PULSES; - data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; - data->REG_ALARM = NCT6775_REG_ALARM; -@@ -1877,6 +1936,7 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; - data->REG_FAN = NCT6779_REG_FAN; - data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; -+ data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES; - data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; - data->REG_ALARM = NCT6779_REG_ALARM; -@@ -2094,6 +2154,10 @@ static int nct6775_probe(struct platform_device *pdev) - if (err) - goto exit_remove; - } -+ err = device_create_file(dev, -+ &sda_fan_pulses[i].dev_attr); -+ if (err) -+ goto exit_remove; - } - } - - -From 47ece9645f288d46420d64dab90a182bde87bbbb Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Tue, 4 Dec 2012 07:59:32 -0800 -Subject: [PATCH] hwmon: (nct6775) Add support for fan debounce module - parameter - -If set, fan debounce is enabled when loading the driver. - -Signed-off-by: Guenter Roeck - -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index fea6ed7..ffb56bb 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -72,6 +72,10 @@ static unsigned short force_id; - module_param(force_id, ushort, 0); - MODULE_PARM_DESC(force_id, "Override the detected device ID"); - -+static unsigned short fan_debounce; -+module_param(fan_debounce, ushort, 0); -+MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); -+ - #define DRVNAME "nct6775" - - /* -@@ -183,6 +187,8 @@ static const u16 NCT6775_REG_IN[] = { - #define NCT6775_REG_FANDIV1 0x506 - #define NCT6775_REG_FANDIV2 0x507 - -+#define NCT6775_REG_CR_FAN_DEBOUNCE 0xf0 -+ - static const u16 NCT6775_REG_ALARM[NUM_REG_ALARM] = { 0x459, 0x45A, 0x45B }; - - /* 0..15 voltages, 16..23 fans, 24..31 temperatures */ -@@ -2110,6 +2116,28 @@ static int nct6775_probe(struct platform_device *pdev) - */ - superio_select(sio_data->sioreg, NCT6775_LD_VID); - data->vid = superio_inb(sio_data->sioreg, 0xe3); -+ -+ if (fan_debounce) { -+ u8 tmp; -+ -+ superio_select(sio_data->sioreg, NCT6775_LD_HWM); -+ tmp = superio_inb(sio_data->sioreg, -+ NCT6775_REG_CR_FAN_DEBOUNCE); -+ switch (data->kind) { -+ case nct6775: -+ tmp |= 0x1e; -+ break; -+ case nct6776: -+ case nct6779: -+ tmp |= 0x3e; -+ break; -+ } -+ superio_outb(sio_data->sioreg, NCT6775_REG_CR_FAN_DEBOUNCE, -+ tmp); -+ dev_info(&pdev->dev, "Enabled fan debounce for chip %s\n", -+ data->name); -+ } -+ - superio_exit(sio_data->sioreg); - - err = device_create_file(dev, &dev_attr_cpu0_vid); - -From 84d19d92f78e10f8bdc1b3e1b5ddcaf5895edaf7 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Tue, 4 Dec 2012 08:01:39 -0800 -Subject: [PATCH] hwmon: (nct6775) Add power management support - -Signed-off-by: Guenter Roeck - -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index ffb56bb..56d7652 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -511,6 +511,12 @@ struct nct6775_data { - u16 have_temp; - u16 have_temp_fixed; - u16 have_in; -+#ifdef CONFIG_PM -+ /* Remember extra register values over suspend/resume */ -+ u8 vbat; -+ u8 fandiv1; -+ u8 fandiv2; -+#endif - }; - - struct nct6775_sio_data { -@@ -2270,10 +2276,90 @@ static int nct6775_remove(struct platform_device *pdev) - return 0; - } - -+#ifdef CONFIG_PM -+static int nct6775_suspend(struct device *dev) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct nct6775_sio_data *sio_data = dev->platform_data; -+ -+ mutex_lock(&data->update_lock); -+ data->vbat = nct6775_read_value(data, data->REG_VBAT); -+ if (sio_data->kind == nct6775) { -+ data->fandiv1 = nct6775_read_value(data, NCT6775_REG_FANDIV1); -+ data->fandiv2 = nct6775_read_value(data, NCT6775_REG_FANDIV2); -+ } -+ mutex_unlock(&data->update_lock); -+ -+ return 0; -+} -+ -+static int nct6775_resume(struct device *dev) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct nct6775_sio_data *sio_data = dev->platform_data; -+ int i, j; -+ -+ mutex_lock(&data->update_lock); -+ data->bank = 0xff; /* Force initial bank selection */ -+ -+ /* Restore limits */ -+ for (i = 0; i < data->in_num; i++) { -+ if (!(data->have_in & (1 << i))) -+ continue; -+ -+ nct6775_write_value(data, data->REG_IN_MINMAX[0][i], -+ data->in[i][1]); -+ nct6775_write_value(data, data->REG_IN_MINMAX[1][i], -+ data->in[i][2]); -+ } -+ -+ for (i = 0; i < 5; i++) { -+ if (!(data->has_fan_min & (1 << i))) -+ continue; -+ -+ nct6775_write_value(data, data->REG_FAN_MIN[i], -+ data->fan_min[i]); -+ } -+ -+ for (i = 0; i < NUM_TEMP; i++) { -+ if (!(data->have_temp & (1 << i))) -+ continue; -+ -+ for (j = 1; j < 4; j++) -+ if (data->reg_temp[j][i]) -+ nct6775_write_temp(data, data->reg_temp[j][i], -+ data->temp[j][i]); -+ } -+ -+ /* Restore other settings */ -+ nct6775_write_value(data, data->REG_VBAT, data->vbat); -+ if (sio_data->kind == nct6775) { -+ nct6775_write_value(data, NCT6775_REG_FANDIV1, data->fandiv1); -+ nct6775_write_value(data, NCT6775_REG_FANDIV2, data->fandiv2); -+ } -+ -+ /* Force re-reading all values */ -+ data->valid = false; -+ mutex_unlock(&data->update_lock); -+ -+ return 0; -+} -+ -+static const struct dev_pm_ops nct6775_dev_pm_ops = { -+ .suspend = nct6775_suspend, -+ .resume = nct6775_resume, -+}; -+ -+#define NCT6775_DEV_PM_OPS (&nct6775_dev_pm_ops) -+#else -+#define NCT6775_DEV_PM_OPS NULL -+#endif /* CONFIG_PM */ -+ - static struct platform_driver nct6775_driver = { - .driver = { - .owner = THIS_MODULE, - .name = DRVNAME, -+ .pm = NCT6775_DEV_PM_OPS, - }, - .probe = nct6775_probe, - .remove = nct6775_remove, - -From 77eb5b3703d995e6c72ef4a1e5411821f81df7e4 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Tue, 4 Dec 2012 08:30:54 -0800 -Subject: [PATCH] hwmon: (nct6775) Add support for pwm, pwm_mode, and - pwm_enable - -Signed-off-by: Guenter Roeck - -diff --git a/Documentation/hwmon/nct6775 b/Documentation/hwmon/nct6775 -index dcd56a3..7c9f1d3 100644 ---- a/Documentation/hwmon/nct6775 -+++ b/Documentation/hwmon/nct6775 -@@ -69,6 +69,24 @@ is driven slower/faster to reach the predefined range again. - - The mode works for fan1-fan5. - -+sysfs attributes -+---------------- -+ -+pwm[1-5] - this file stores PWM duty cycle or DC value (fan speed) in range: -+ 0 (lowest speed) to 255 (full) -+ -+pwm[1-5]_enable - this file controls mode of fan/temperature control: -+ * 0 Fan control disabled (fans set to maximum speed) -+ * 1 Manual mode, write to pwm[0-5] any value 0-255 -+ * 2 "Thermal Cruise" mode -+ * 3 "Fan Speed Cruise" mode -+ * 4 "Smart Fan III" mode (NCT6775F only) -+ * 5 "Smart Fan IV" mode -+ -+pwm[1-5]_mode - controls if output is PWM or DC level -+ * 0 DC output -+ * 1 PWM output -+ - Usage Notes - ----------- - -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index 56d7652..ad4ecc0 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -96,6 +96,8 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); - #define SIO_NCT6779_ID 0xc560 - #define SIO_ID_MASK 0xFFF0 - -+enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; -+ - static inline void - superio_outb(int ioreg, int reg, int val) - { -@@ -209,6 +211,15 @@ static const s8 NCT6775_ALARM_BITS[] = { - static const u8 NCT6775_REG_CR_CASEOPEN_CLR[] = { 0xe6, 0xee }; - static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 }; - -+/* DC or PWM output fan configuration */ -+static const u8 NCT6775_REG_PWM_MODE[] = { 0x04, 0x04, 0x12 }; -+static const u8 NCT6775_PWM_MODE_MASK[] = { 0x01, 0x02, 0x01 }; -+ -+static const u16 NCT6775_REG_FAN_MODE[] = { 0x102, 0x202, 0x302, 0x802, 0x902 }; -+ -+static const u16 NCT6775_REG_PWM[] = { 0x109, 0x209, 0x309, 0x809, 0x909 }; -+static const u16 NCT6775_REG_PWM_READ[] = { 0x01, 0x03, 0x11, 0x13, 0x15 }; -+ - static const u16 NCT6775_REG_FAN[] = { 0x630, 0x632, 0x634, 0x636, 0x638 }; - static const u16 NCT6775_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d }; - static const u16 NCT6775_REG_FAN_PULSES[] = { 0x641, 0x642, 0x643, 0x644, 0 }; -@@ -270,6 +281,9 @@ static const s8 NCT6776_ALARM_BITS[] = { - 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ - 12, 9 }; /* intrusion0, intrusion1 */ - -+static const u8 NCT6776_REG_PWM_MODE[] = { 0x04, 0, 0 }; -+static const u8 NCT6776_PWM_MODE_MASK[] = { 0x01, 0, 0 }; -+ - static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642 }; - static const u16 NCT6776_REG_FAN_PULSES[] = { 0x644, 0x645, 0x646, 0, 0 }; - -@@ -380,6 +394,20 @@ static const u16 NCT6779_REG_TEMP_ALTERNATE[ARRAY_SIZE(nct6779_temp_label) - 1] - static const u16 NCT6779_REG_TEMP_CRIT[ARRAY_SIZE(nct6779_temp_label) - 1] - = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x709, 0x70a }; - -+static enum pwm_enable reg_to_pwm_enable(int pwm, int mode) -+{ -+ if (mode == 0 && pwm == 255) -+ return off; -+ return mode + 1; -+} -+ -+static int pwm_enable_to_reg(enum pwm_enable mode) -+{ -+ if (mode == off) -+ return 0; -+ return mode - 1; -+} -+ - /* - * Conversions - */ -@@ -471,9 +499,16 @@ struct nct6775_data { - const u16 *REG_IN_MINMAX[2]; - - const u16 *REG_FAN; -+ const u16 *REG_FAN_MODE; - const u16 *REG_FAN_MIN; - const u16 *REG_FAN_PULSES; - -+ const u8 *REG_PWM_MODE; -+ const u8 *PWM_MODE_MASK; -+ -+ const u16 *REG_PWM[1]; /* [0]=pwm */ -+ const u16 *REG_PWM_READ; -+ - const u16 *REG_TEMP_SOURCE; /* temp register sources */ - const u16 *REG_TEMP_OFFSET; - -@@ -494,6 +529,7 @@ struct nct6775_data { - u16 fan_min[5]; - u8 fan_pulses[5]; - u8 fan_div[5]; -+ u8 has_pwm; - u8 has_fan; /* some fan inputs can be disabled */ - u8 has_fan_min; /* some fans don't have min register */ - bool has_fan_div; -@@ -505,6 +541,18 @@ struct nct6775_data { - * 3=temp_crit */ - u64 alarms; - -+ u8 pwm_num; /* number of pwm */ -+ u8 pwm_mode[5]; /* 1->DC variable voltage, 0->PWM variable duty cycle */ -+ enum pwm_enable pwm_enable[5]; -+ /* 0->off -+ * 1->manual -+ * 2->thermal cruise mode (also called SmartFan I) -+ * 3->fan speed cruise mode -+ * 4->SmartFan III -+ * 5->enhanced variable thermal cruise (SmartFan IV) -+ */ -+ u8 pwm[1][5]; /* [0]=pwm */ -+ - u8 vid; - u8 vrm; - -@@ -781,6 +829,36 @@ static void nct6775_select_fan_div(struct device *dev, - } - } - -+static void nct6775_update_pwm(struct device *dev) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ int i, j; -+ int fanmodecfg; -+ bool duty_is_dc; -+ -+ for (i = 0; i < data->pwm_num; i++) { -+ if (!(data->has_pwm & (1 << i))) -+ continue; -+ -+ duty_is_dc = data->REG_PWM_MODE[i] && -+ (nct6775_read_value(data, data->REG_PWM_MODE[i]) -+ & data->PWM_MODE_MASK[i]); -+ data->pwm_mode[i] = duty_is_dc; -+ -+ fanmodecfg = nct6775_read_value(data, data->REG_FAN_MODE[i]); -+ for (j = 0; j < ARRAY_SIZE(data->REG_PWM); j++) { -+ if (data->REG_PWM[j] && data->REG_PWM[j][i]) { -+ data->pwm[j][i] -+ = nct6775_read_value(data, -+ data->REG_PWM[j][i]); -+ } -+ } -+ -+ data->pwm_enable[i] = reg_to_pwm_enable(data->pwm[0][i], -+ (fanmodecfg >> 4) & 7); -+ } -+} -+ - static struct nct6775_data *nct6775_update_device(struct device *dev) - { - struct nct6775_data *data = dev_get_drvdata(dev); -@@ -826,6 +904,8 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) - nct6775_select_fan_div(dev, data, i, reg); - } - -+ nct6775_update_pwm(dev); -+ - /* Measured temperatures and limits */ - for (i = 0; i < NUM_TEMP; i++) { - if (!(data->have_temp & (1 << i))) -@@ -1600,6 +1680,170 @@ static struct sensor_device_attribute sda_temp_alarm[] = { - #define NUM_TEMP_ALARM ARRAY_SIZE(sda_temp_alarm) - - static ssize_t -+show_pwm_mode(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ -+ return sprintf(buf, "%d\n", !data->pwm_mode[sattr->index]); -+} -+ -+static ssize_t -+store_pwm_mode(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ unsigned long val; -+ int err; -+ u8 reg; -+ -+ err = kstrtoul(buf, 10, &val); -+ if (err < 0) -+ return err; -+ -+ if (val > 1) -+ return -EINVAL; -+ -+ /* Setting DC mode is not supported for all chips/channels */ -+ if (data->REG_PWM_MODE[nr] == 0) { -+ if (val) -+ return -EINVAL; -+ return count; -+ } -+ -+ mutex_lock(&data->update_lock); -+ data->pwm_mode[nr] = val; -+ reg = nct6775_read_value(data, data->REG_PWM_MODE[nr]); -+ reg &= ~data->PWM_MODE_MASK[nr]; -+ if (val) -+ reg |= data->PWM_MODE_MASK[nr]; -+ nct6775_write_value(data, data->REG_PWM_MODE[nr], reg); -+ mutex_unlock(&data->update_lock); -+ return count; -+} -+ -+static ssize_t -+show_pwm(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); -+ int nr = sattr->nr; -+ int index = sattr->index; -+ int pwm; -+ -+ /* -+ * For automatic fan control modes, show current pwm readings. -+ * Otherwise, show the configured value. -+ */ -+ if (index == 0 && data->pwm_enable[nr] > manual) -+ pwm = nct6775_read_value(data, data->REG_PWM_READ[nr]); -+ else -+ pwm = data->pwm[index][nr]; -+ -+ return sprintf(buf, "%d\n", pwm); -+} -+ -+static ssize_t -+store_pwm(struct device *dev, struct device_attribute *attr, const char *buf, -+ size_t count) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); -+ int nr = sattr->nr; -+ int index = sattr->index; -+ unsigned long val; -+ int err; -+ -+ err = kstrtoul(buf, 10, &val); -+ if (err < 0) -+ return err; -+ val = clamp_val(val, 0, 255); -+ -+ mutex_lock(&data->update_lock); -+ data->pwm[index][nr] = val; -+ nct6775_write_value(data, data->REG_PWM[index][nr], val); -+ mutex_unlock(&data->update_lock); -+ return count; -+} -+ -+static ssize_t -+show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ -+ return sprintf(buf, "%d\n", data->pwm_enable[sattr->index]); -+} -+ -+static ssize_t -+store_pwm_enable(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ unsigned long val; -+ int err; -+ u16 reg; -+ -+ err = kstrtoul(buf, 10, &val); -+ if (err < 0) -+ return err; -+ -+ if (val > sf4) -+ return -EINVAL; -+ -+ if (val == sf3 && data->kind != nct6775) -+ return -EINVAL; -+ -+ mutex_lock(&data->update_lock); -+ data->pwm_enable[nr] = val; -+ if (val == off) { -+ /* -+ * turn off pwm control: select manual mode, set pwm to maximum -+ */ -+ data->pwm[0][nr] = 255; -+ nct6775_write_value(data, data->REG_PWM[0][nr], 255); -+ } -+ reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]); -+ reg &= 0x0f; -+ reg |= pwm_enable_to_reg(val) << 4; -+ nct6775_write_value(data, data->REG_FAN_MODE[nr], reg); -+ mutex_unlock(&data->update_lock); -+ return count; -+} -+ -+static SENSOR_DEVICE_ATTR_2(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 0); -+static SENSOR_DEVICE_ATTR_2(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1, 0); -+static SENSOR_DEVICE_ATTR_2(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2, 0); -+static SENSOR_DEVICE_ATTR_2(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3, 0); -+static SENSOR_DEVICE_ATTR_2(pwm5, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 4, 0); -+ -+static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode, -+ store_pwm_mode, 0); -+static SENSOR_DEVICE_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode, -+ store_pwm_mode, 1); -+static SENSOR_DEVICE_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode, -+ store_pwm_mode, 2); -+static SENSOR_DEVICE_ATTR(pwm4_mode, S_IWUSR | S_IRUGO, show_pwm_mode, -+ store_pwm_mode, 3); -+static SENSOR_DEVICE_ATTR(pwm5_mode, S_IWUSR | S_IRUGO, show_pwm_mode, -+ store_pwm_mode, 4); -+ -+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable, -+ store_pwm_enable, 0); -+static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable, -+ store_pwm_enable, 1); -+static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable, -+ store_pwm_enable, 2); -+static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_enable, -+ store_pwm_enable, 3); -+static SENSOR_DEVICE_ATTR(pwm5_enable, S_IWUSR | S_IRUGO, show_pwm_enable, -+ store_pwm_enable, 4); -+ -+static ssize_t - show_name(struct device *dev, struct device_attribute *attr, char *buf) - { - struct nct6775_data *data = dev_get_drvdata(dev); -@@ -1609,6 +1853,47 @@ show_name(struct device *dev, struct device_attribute *attr, char *buf) - - static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); - -+static struct attribute *nct6775_attributes_pwm[5][4] = { -+ { -+ &sensor_dev_attr_pwm1.dev_attr.attr, -+ &sensor_dev_attr_pwm1_mode.dev_attr.attr, -+ &sensor_dev_attr_pwm1_enable.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_pwm2.dev_attr.attr, -+ &sensor_dev_attr_pwm2_mode.dev_attr.attr, -+ &sensor_dev_attr_pwm2_enable.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_pwm3.dev_attr.attr, -+ &sensor_dev_attr_pwm3_mode.dev_attr.attr, -+ &sensor_dev_attr_pwm3_enable.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_pwm4.dev_attr.attr, -+ &sensor_dev_attr_pwm4_mode.dev_attr.attr, -+ &sensor_dev_attr_pwm4_enable.dev_attr.attr, -+ NULL -+ }, -+ { -+ &sensor_dev_attr_pwm5.dev_attr.attr, -+ &sensor_dev_attr_pwm5_mode.dev_attr.attr, -+ &sensor_dev_attr_pwm5_enable.dev_attr.attr, -+ NULL -+ }, -+}; -+ -+static const struct attribute_group nct6775_group_pwm[5] = { -+ { .attrs = nct6775_attributes_pwm[0] }, -+ { .attrs = nct6775_attributes_pwm[1] }, -+ { .attrs = nct6775_attributes_pwm[2] }, -+ { .attrs = nct6775_attributes_pwm[3] }, -+ { .attrs = nct6775_attributes_pwm[4] }, -+}; -+ - static ssize_t - show_vid(struct device *dev, struct device_attribute *attr, char *buf) - { -@@ -1681,6 +1966,9 @@ static void nct6775_device_remove_files(struct device *dev) - int i; - struct nct6775_data *data = dev_get_drvdata(dev); - -+ for (i = 0; i < data->pwm_num; i++) -+ sysfs_remove_group(&dev->kobj, &nct6775_group_pwm[i]); -+ - for (i = 0; i < data->in_num; i++) - sysfs_remove_group(&dev->kobj, &nct6775_group_in[i]); - -@@ -1763,6 +2051,7 @@ nct6775_check_fan_inputs(const struct nct6775_sio_data *sio_data, - { - int regval; - bool fan3pin, fan3min, fan4pin, fan4min, fan5pin; -+ bool pwm3pin, pwm4pin, pwm5pin; - int ret; - - ret = superio_enter(sio_data->sioreg); -@@ -1775,11 +2064,14 @@ nct6775_check_fan_inputs(const struct nct6775_sio_data *sio_data, - - fan3pin = regval & (1 << 6); - fan3min = fan3pin; -+ pwm3pin = regval & (1 << 7); - - /* On NCT6775, fan4 shares pins with the fdc interface */ - fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80); - fan4min = 0; - fan5pin = 0; -+ pwm4pin = 0; -+ pwm5pin = 0; - } else if (data->kind == nct6776) { - bool gpok = superio_inb(sio_data->sioreg, 0x27) & 0x80; - -@@ -1803,6 +2095,9 @@ nct6775_check_fan_inputs(const struct nct6775_sio_data *sio_data, - - fan4min = fan4pin; - fan3min = fan3pin; -+ pwm3pin = fan3pin; -+ pwm4pin = 0; -+ pwm5pin = 0; - } else { /* NCT6779D */ - regval = superio_inb(sio_data->sioreg, 0x1c); - -@@ -1810,6 +2105,10 @@ nct6775_check_fan_inputs(const struct nct6775_sio_data *sio_data, - fan4pin = !(regval & (1 << 6)); - fan5pin = !(regval & (1 << 7)); - -+ pwm3pin = !(regval & (1 << 0)); -+ pwm4pin = !(regval & (1 << 1)); -+ pwm5pin = !(regval & (1 << 2)); -+ - fan3min = fan3pin; - fan4min = fan4pin; - } -@@ -1823,6 +2122,8 @@ nct6775_check_fan_inputs(const struct nct6775_sio_data *sio_data, - data->has_fan |= (fan4pin << 3) | (fan5pin << 4); - data->has_fan_min |= (fan4min << 3) | (fan5pin << 4); - -+ data->has_pwm = 0x03 | (pwm3pin << 2) | (pwm4pin << 3) | (pwm5pin << 4); -+ - return 0; - } - -@@ -1859,6 +2160,7 @@ static int nct6775_probe(struct platform_device *pdev) - switch (data->kind) { - case nct6775: - data->in_num = 9; -+ data->pwm_num = 3; - data->has_fan_div = true; - data->temp_fixed_num = 3; - -@@ -1877,8 +2179,13 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; - data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; - data->REG_FAN = NCT6775_REG_FAN; -+ data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; - data->REG_FAN_MIN = NCT6775_REG_FAN_MIN; - data->REG_FAN_PULSES = NCT6775_REG_FAN_PULSES; -+ data->REG_PWM[0] = NCT6775_REG_PWM; -+ data->REG_PWM_READ = NCT6775_REG_PWM_READ; -+ data->REG_PWM_MODE = NCT6775_REG_PWM_MODE; -+ data->PWM_MODE_MASK = NCT6775_PWM_MODE_MASK; - data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; - data->REG_ALARM = NCT6775_REG_ALARM; -@@ -1894,6 +2201,7 @@ static int nct6775_probe(struct platform_device *pdev) - break; - case nct6776: - data->in_num = 9; -+ data->pwm_num = 3; - data->has_fan_div = false; - data->temp_fixed_num = 3; - -@@ -1912,8 +2220,13 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; - data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; - data->REG_FAN = NCT6775_REG_FAN; -+ data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; - data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; - data->REG_FAN_PULSES = NCT6776_REG_FAN_PULSES; -+ data->REG_PWM[0] = NCT6775_REG_PWM; -+ data->REG_PWM_READ = NCT6775_REG_PWM_READ; -+ data->REG_PWM_MODE = NCT6776_REG_PWM_MODE; -+ data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK; - data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; - data->REG_ALARM = NCT6775_REG_ALARM; -@@ -1929,6 +2242,7 @@ static int nct6775_probe(struct platform_device *pdev) - break; - case nct6779: - data->in_num = 15; -+ data->pwm_num = 5; - data->has_fan_div = false; - data->temp_fixed_num = 6; - -@@ -1947,8 +2261,13 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; - data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; - data->REG_FAN = NCT6779_REG_FAN; -+ data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; - data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; - data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES; -+ data->REG_PWM[0] = NCT6775_REG_PWM; -+ data->REG_PWM_READ = NCT6775_REG_PWM_READ; -+ data->REG_PWM_MODE = NCT6776_REG_PWM_MODE; -+ data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK; - data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; - data->REG_ALARM = NCT6779_REG_ALARM; -@@ -2157,6 +2476,16 @@ static int nct6775_probe(struct platform_device *pdev) - /* Read fan clock dividers immediately */ - nct6775_init_fan_common(dev, data); - -+ /* Register sysfs hooks */ -+ for (i = 0; i < data->pwm_num; i++) { -+ if (!(data->has_pwm & (1 << i))) -+ continue; -+ -+ err = sysfs_create_group(&dev->kobj, &nct6775_group_pwm[i]); -+ if (err) -+ goto exit_remove; -+ } -+ - for (i = 0; i < data->in_num; i++) { - if (!(data->have_in & (1 << i))) - continue; - -From cdcaeceb74ff3686eb25de6812870fbc765c3c39 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Tue, 4 Dec 2012 09:04:52 -0800 -Subject: [PATCH] hwmon: (nct6775) Add support for automatic fan control - -Signed-off-by: Guenter Roeck - -diff --git a/Documentation/hwmon/nct6775 b/Documentation/hwmon/nct6775 -index 7c9f1d3..b4fe6bc 100644 ---- a/Documentation/hwmon/nct6775 -+++ b/Documentation/hwmon/nct6775 -@@ -87,6 +87,75 @@ pwm[1-5]_mode - controls if output is PWM or DC level - * 0 DC output - * 1 PWM output - -+Common fan control attributes -+----------------------------- -+ -+pwm[1-5]_temp_sel Temperature source. Value is temperature sensor index. -+ For example, select '1' for temp1_input. -+ -+Thermal Cruise mode (2) -+----------------------- -+ -+If the temperature is in the range defined by: -+ -+pwm[1-5]_target_temp Target temperature, unit millidegree Celsius -+ (range 0 - 127000) -+pwm[1-5]_temp_tolerance -+ Target temperature tolerance, unit millidegree Celsius -+ -+there are no changes to fan speed. Once the temperature leaves the interval, fan -+speed increases (if temperature is higher that desired) or decreases (if -+temperature is lower than desired), using the following limits and time -+intervals. -+ -+pwm[1-5]_start fan pwm start value (range 1 - 255), to start fan -+ when the temperature is above defined range. -+pwm[1-5]_floor lowest fan pwm (range 0 - 255) if temperature is below -+ the defined range. If set to 0, the fan is expected to -+ stop if the temperature is below the defined range. -+pwm[1-5]_step_up_time milliseconds before fan speed is increased -+pwm[1-5]_step_down_time milliseconds before fan speed is decreased -+pwm[1-5]_stop_time how many milliseconds must elapse to switch -+ corresponding fan off (when the temperature was below -+ defined range). -+ -+Speed Cruise mode (3) -+--------------------- -+ -+This modes tries to keep the fan speed constant. -+ -+fan[1-5]_target Target fan speed -+fan[1-5]_tolerance -+ Target speed tolerance -+ -+ -+Untested; use at your own risk. -+ -+Smart Fan IV mode (5) -+--------------------- -+ -+This mode offers multiple slopes to control the fan speed. The slopes can be -+controlled by setting the pwm and temperature attributes. When the temperature -+rises, the chip will calculate the DC/PWM output based on the current slope. -+There are up to seven data points depending on the chip type. Subsequent data -+points should be set to higher temperatures and higher pwm values to achieve -+higher fan speeds with increasing temperature. The last data point reflects -+critical temperature mode, in which the fans should run at full speed. -+ -+pwm[1-5]_auto_point[1-7]_pwm -+ pwm value to be set if temperature reaches matching -+ temperature range. -+pwm[1-5]_auto_point[1-7]_temp -+ Temperature over which the matching pwm is enabled. -+pwm[1-5]_temp_tolerance -+ Temperature tolerance, unit millidegree Celsius -+pwm[1-5]_crit_temp_tolerance -+ Temperature tolerance for critical temperature, -+ unit millidegree Celsius -+ -+pwm[1-5]_step_up_time milliseconds before fan speed is increased -+pwm[1-5]_step_down_time milliseconds before fan speed is decreased -+ - Usage Notes - ----------- - -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index ad4ecc0..47b1d89 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -215,8 +215,23 @@ static const u8 NCT6775_CR_CASEOPEN_CLR_MASK[] = { 0x20, 0x01 }; - static const u8 NCT6775_REG_PWM_MODE[] = { 0x04, 0x04, 0x12 }; - static const u8 NCT6775_PWM_MODE_MASK[] = { 0x01, 0x02, 0x01 }; - --static const u16 NCT6775_REG_FAN_MODE[] = { 0x102, 0x202, 0x302, 0x802, 0x902 }; -+/* Advanced Fan control, some values are common for all fans */ - -+static const u16 NCT6775_REG_TARGET[] = { 0x101, 0x201, 0x301, 0x801, 0x901 }; -+static const u16 NCT6775_REG_FAN_MODE[] = { 0x102, 0x202, 0x302, 0x802, 0x902 }; -+static const u16 NCT6775_REG_FAN_STEP_DOWN_TIME[] = { -+ 0x103, 0x203, 0x303, 0x803, 0x903 }; -+static const u16 NCT6775_REG_FAN_STEP_UP_TIME[] = { -+ 0x104, 0x204, 0x304, 0x804, 0x904 }; -+static const u16 NCT6775_REG_FAN_STOP_OUTPUT[] = { -+ 0x105, 0x205, 0x305, 0x805, 0x905 }; -+static const u16 NCT6775_REG_FAN_START_OUTPUT[] -+ = { 0x106, 0x206, 0x306, 0x806, 0x906 }; -+static const u16 NCT6775_REG_FAN_MAX_OUTPUT[] = { 0x10a, 0x20a, 0x30a }; -+static const u16 NCT6775_REG_FAN_STEP_OUTPUT[] = { 0x10b, 0x20b, 0x30b }; -+ -+static const u16 NCT6775_REG_FAN_STOP_TIME[] = { -+ 0x107, 0x207, 0x307, 0x807, 0x907 }; - static const u16 NCT6775_REG_PWM[] = { 0x109, 0x209, 0x309, 0x809, 0x909 }; - static const u16 NCT6775_REG_PWM_READ[] = { 0x01, 0x03, 0x11, 0x13, 0x15 }; - -@@ -237,8 +252,26 @@ static const u16 NCT6775_REG_TEMP_OVER[ARRAY_SIZE(NCT6775_REG_TEMP)] = { - static const u16 NCT6775_REG_TEMP_SOURCE[ARRAY_SIZE(NCT6775_REG_TEMP)] = { - 0x621, 0x622, 0x623, 0x624, 0x625, 0x626 }; - -+static const u16 NCT6775_REG_TEMP_SEL[] = { -+ 0x100, 0x200, 0x300, 0x800, 0x900 }; -+ - static const u16 NCT6775_REG_TEMP_OFFSET[] = { 0x454, 0x455, 0x456 }; - -+static const u16 NCT6775_REG_AUTO_TEMP[] = { -+ 0x121, 0x221, 0x321, 0x821, 0x921 }; -+static const u16 NCT6775_REG_AUTO_PWM[] = { -+ 0x127, 0x227, 0x327, 0x827, 0x927 }; -+ -+#define NCT6775_AUTO_TEMP(data, nr, p) ((data)->REG_AUTO_TEMP[nr] + (p)) -+#define NCT6775_AUTO_PWM(data, nr, p) ((data)->REG_AUTO_PWM[nr] + (p)) -+ -+static const u16 NCT6775_REG_CRITICAL_ENAB[] = { 0x134, 0x234, 0x334 }; -+ -+static const u16 NCT6775_REG_CRITICAL_TEMP[] = { -+ 0x135, 0x235, 0x335, 0x835, 0x935 }; -+static const u16 NCT6775_REG_CRITICAL_TEMP_TOLERANCE[] = { -+ 0x138, 0x238, 0x338, 0x838, 0x938 }; -+ - static const char *const nct6775_temp_label[] = { - "", - "SYSTIN", -@@ -281,6 +314,9 @@ static const s8 NCT6776_ALARM_BITS[] = { - 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ - 12, 9 }; /* intrusion0, intrusion1 */ - -+static const u16 NCT6776_REG_TOLERANCE_H[] = { -+ 0x10c, 0x20c, 0x30c, 0x80c, 0x90c }; -+ - static const u8 NCT6776_REG_PWM_MODE[] = { 0x04, 0, 0 }; - static const u8 NCT6776_PWM_MODE_MASK[] = { 0x01, 0, 0 }; - -@@ -344,6 +380,11 @@ static const u16 NCT6779_REG_FAN[] = { 0x4b0, 0x4b2, 0x4b4, 0x4b6, 0x4b8 }; - static const u16 NCT6779_REG_FAN_PULSES[] = { - 0x644, 0x645, 0x646, 0x647, 0x648 }; - -+static const u16 NCT6779_REG_CRITICAL_PWM_ENABLE[] = { -+ 0x136, 0x236, 0x336, 0x836, 0x936 }; -+static const u16 NCT6779_REG_CRITICAL_PWM[] = { -+ 0x137, 0x237, 0x337, 0x837, 0x937 }; -+ - static const u16 NCT6779_REG_TEMP[] = { 0x27, 0x150 }; - static const u16 NCT6779_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6779_REG_TEMP)] = { - 0x18, 0x152 }; -@@ -412,6 +453,18 @@ static int pwm_enable_to_reg(enum pwm_enable mode) - * Conversions - */ - -+/* 1 is DC mode, output in ms */ -+static unsigned int step_time_from_reg(u8 reg, u8 mode) -+{ -+ return mode ? 400 * reg : 100 * reg; -+} -+ -+static u8 step_time_to_reg(unsigned int msec, u8 mode) -+{ -+ return clamp_val((mode ? (msec + 200) / 400 : -+ (msec + 50) / 100), 1, 255); -+} -+ - static unsigned int fan_from_reg8(u16 reg, unsigned int divreg) - { - if (reg == 0 || reg == 255) -@@ -444,6 +497,14 @@ static unsigned int fan_from_reg16(u16 reg, unsigned int divreg) - return 1350000U / (reg << divreg); - } - -+static u16 fan_to_reg(u32 fan, unsigned int divreg) -+{ -+ if (!fan) -+ return 0; -+ -+ return (1350000U / fan) >> divreg; -+} -+ - static inline unsigned int - div_from_reg(u8 reg) - { -@@ -498,18 +559,31 @@ struct nct6775_data { - const u16 *REG_VIN; - const u16 *REG_IN_MINMAX[2]; - -+ const u16 *REG_TARGET; - const u16 *REG_FAN; - const u16 *REG_FAN_MODE; - const u16 *REG_FAN_MIN; - const u16 *REG_FAN_PULSES; -+ const u16 *REG_FAN_TIME[3]; -+ -+ const u16 *REG_TOLERANCE_H; - - const u8 *REG_PWM_MODE; - const u8 *PWM_MODE_MASK; - -- const u16 *REG_PWM[1]; /* [0]=pwm */ -+ const u16 *REG_PWM[5]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor, -+ * [3]=pwm_max, [4]=pwm_step -+ */ - const u16 *REG_PWM_READ; - -+ const u16 *REG_AUTO_TEMP; -+ const u16 *REG_AUTO_PWM; -+ -+ const u16 *REG_CRITICAL_TEMP; -+ const u16 *REG_CRITICAL_TEMP_TOLERANCE; -+ - const u16 *REG_TEMP_SOURCE; /* temp register sources */ -+ const u16 *REG_TEMP_SEL; - const u16 *REG_TEMP_OFFSET; - - const u16 *REG_ALARM; -@@ -551,7 +625,26 @@ struct nct6775_data { - * 4->SmartFan III - * 5->enhanced variable thermal cruise (SmartFan IV) - */ -- u8 pwm[1][5]; /* [0]=pwm */ -+ u8 pwm[5][5]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor, -+ * [3]=pwm_max, [4]=pwm_step -+ */ -+ -+ u8 target_temp[5]; -+ u8 target_temp_mask; -+ u32 target_speed[5]; -+ u32 target_speed_tolerance[5]; -+ u8 speed_tolerance_limit; -+ -+ u8 temp_tolerance[2][5]; -+ u8 tolerance_mask; -+ -+ u8 fan_time[3][5]; /* 0 = stop_time, 1 = step_up, 2 = step_down */ -+ -+ /* Automatic fan speed control registers */ -+ int auto_pwm_num; -+ u8 auto_pwm[5][7]; -+ u8 auto_temp[5][7]; -+ u8 pwm_temp_sel[5]; - - u8 vid; - u8 vrm; -@@ -833,7 +926,7 @@ static void nct6775_update_pwm(struct device *dev) - { - struct nct6775_data *data = dev_get_drvdata(dev); - int i, j; -- int fanmodecfg; -+ int fanmodecfg, reg; - bool duty_is_dc; - - for (i = 0; i < data->pwm_num; i++) { -@@ -856,6 +949,96 @@ static void nct6775_update_pwm(struct device *dev) - - data->pwm_enable[i] = reg_to_pwm_enable(data->pwm[0][i], - (fanmodecfg >> 4) & 7); -+ -+ if (!data->temp_tolerance[0][i] || -+ data->pwm_enable[i] != speed_cruise) -+ data->temp_tolerance[0][i] = fanmodecfg & 0x0f; -+ if (!data->target_speed_tolerance[i] || -+ data->pwm_enable[i] == speed_cruise) { -+ u8 t = fanmodecfg & 0x0f; -+ if (data->REG_TOLERANCE_H) { -+ t |= (nct6775_read_value(data, -+ data->REG_TOLERANCE_H[i]) & 0x70) >> 1; -+ } -+ data->target_speed_tolerance[i] = t; -+ } -+ -+ data->temp_tolerance[1][i] = -+ nct6775_read_value(data, -+ data->REG_CRITICAL_TEMP_TOLERANCE[i]); -+ -+ reg = nct6775_read_value(data, data->REG_TEMP_SEL[i]); -+ data->pwm_temp_sel[i] = reg & 0x1f; -+ /* If fan can stop, report floor as 0 */ -+ if (reg & 0x80) -+ data->pwm[2][i] = 0; -+ } -+} -+ -+static void nct6775_update_pwm_limits(struct device *dev) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ int i, j; -+ u8 reg; -+ u16 reg_t; -+ -+ for (i = 0; i < data->pwm_num; i++) { -+ if (!(data->has_pwm & (1 << i))) -+ continue; -+ -+ for (j = 0; j < 3; j++) { -+ data->fan_time[j][i] = -+ nct6775_read_value(data, data->REG_FAN_TIME[j][i]); -+ } -+ -+ reg_t = nct6775_read_value(data, data->REG_TARGET[i]); -+ /* Update only in matching mode or if never updated */ -+ if (!data->target_temp[i] || -+ data->pwm_enable[i] == thermal_cruise) -+ data->target_temp[i] = reg_t & data->target_temp_mask; -+ if (!data->target_speed[i] || -+ data->pwm_enable[i] == speed_cruise) { -+ if (data->REG_TOLERANCE_H) { -+ reg_t |= (nct6775_read_value(data, -+ data->REG_TOLERANCE_H[i]) & 0x0f) << 8; -+ } -+ data->target_speed[i] = reg_t; -+ } -+ -+ for (j = 0; j < data->auto_pwm_num; j++) { -+ data->auto_pwm[i][j] = -+ nct6775_read_value(data, -+ NCT6775_AUTO_PWM(data, i, j)); -+ data->auto_temp[i][j] = -+ nct6775_read_value(data, -+ NCT6775_AUTO_TEMP(data, i, j)); -+ } -+ -+ /* critical auto_pwm temperature data */ -+ data->auto_temp[i][data->auto_pwm_num] = -+ nct6775_read_value(data, data->REG_CRITICAL_TEMP[i]); -+ -+ switch (data->kind) { -+ case nct6775: -+ reg = nct6775_read_value(data, -+ NCT6775_REG_CRITICAL_ENAB[i]); -+ data->auto_pwm[i][data->auto_pwm_num] = -+ (reg & 0x02) ? 0xff : 0x00; -+ break; -+ case nct6776: -+ data->auto_pwm[i][data->auto_pwm_num] = 0xff; -+ break; -+ case nct6779: -+ reg = nct6775_read_value(data, -+ NCT6779_REG_CRITICAL_PWM_ENABLE[i]); -+ if (reg & 1) -+ data->auto_pwm[i][data->auto_pwm_num] = -+ nct6775_read_value(data, -+ NCT6779_REG_CRITICAL_PWM[i]); -+ else -+ data->auto_pwm[i][data->auto_pwm_num] = 0xff; -+ break; -+ } - } - } - -@@ -905,6 +1088,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) - } - - nct6775_update_pwm(dev); -+ nct6775_update_pwm_limits(dev); - - /* Measured temperatures and limits */ - for (i = 0; i < NUM_TEMP; i++) { -@@ -1754,20 +1938,91 @@ store_pwm(struct device *dev, struct device_attribute *attr, const char *buf, - int nr = sattr->nr; - int index = sattr->index; - unsigned long val; -+ int minval[5] = { 0, 1, 1, data->pwm[2][nr], 0 }; -+ int maxval[5] -+ = { 255, 255, data->pwm[3][nr] ? : 255, 255, 255 }; - int err; -+ u8 reg; - - err = kstrtoul(buf, 10, &val); - if (err < 0) - return err; -- val = clamp_val(val, 0, 255); -+ val = clamp_val(val, minval[index], maxval[index]); - - mutex_lock(&data->update_lock); - data->pwm[index][nr] = val; - nct6775_write_value(data, data->REG_PWM[index][nr], val); -+ if (index == 2) { /* floor: disable if val == 0 */ -+ reg = nct6775_read_value(data, data->REG_TEMP_SEL[nr]); -+ reg &= 0x7f; -+ if (val) -+ reg |= 0x80; -+ nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg); -+ } - mutex_unlock(&data->update_lock); - return count; - } - -+/* Returns 0 if OK, -EINVAL otherwise */ -+static int check_trip_points(struct nct6775_data *data, int nr) -+{ -+ int i; -+ -+ for (i = 0; i < data->auto_pwm_num - 1; i++) { -+ if (data->auto_temp[nr][i] > data->auto_temp[nr][i + 1]) -+ return -EINVAL; -+ } -+ for (i = 0; i < data->auto_pwm_num - 1; i++) { -+ if (data->auto_pwm[nr][i] > data->auto_pwm[nr][i + 1]) -+ return -EINVAL; -+ } -+ /* validate critical temperature and pwm if enabled (pwm > 0) */ -+ if (data->auto_pwm[nr][data->auto_pwm_num]) { -+ if (data->auto_temp[nr][data->auto_pwm_num - 1] > -+ data->auto_temp[nr][data->auto_pwm_num] || -+ data->auto_pwm[nr][data->auto_pwm_num - 1] > -+ data->auto_pwm[nr][data->auto_pwm_num]) -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+static void pwm_update_registers(struct nct6775_data *data, int nr) -+{ -+ u8 reg; -+ -+ switch (data->pwm_enable[nr]) { -+ case off: -+ case manual: -+ break; -+ case speed_cruise: -+ reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]); -+ reg = (reg & ~data->tolerance_mask) | -+ (data->target_speed_tolerance[nr] & data->tolerance_mask); -+ nct6775_write_value(data, data->REG_FAN_MODE[nr], reg); -+ nct6775_write_value(data, data->REG_TARGET[nr], -+ data->target_speed[nr] & 0xff); -+ if (data->REG_TOLERANCE_H) { -+ reg = (data->target_speed[nr] >> 8) & 0x0f; -+ reg |= (data->target_speed_tolerance[nr] & 0x38) << 1; -+ nct6775_write_value(data, -+ data->REG_TOLERANCE_H[nr], -+ reg); -+ } -+ break; -+ case thermal_cruise: -+ nct6775_write_value(data, data->REG_TARGET[nr], -+ data->target_temp[nr]); -+ /* intentional */ -+ default: -+ reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]); -+ reg = (reg & ~data->tolerance_mask) | -+ data->temp_tolerance[0][nr]; -+ nct6775_write_value(data, data->REG_FAN_MODE[nr], reg); -+ break; -+ } -+} -+ - static ssize_t - show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf) - { -@@ -1798,6 +2053,12 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr, - if (val == sf3 && data->kind != nct6775) - return -EINVAL; - -+ if (val == sf4 && check_trip_points(data, nr)) { -+ dev_err(dev, "Inconsistent trip points, not switching to SmartFan IV mode\n"); -+ dev_err(dev, "Adjust trip points and try again\n"); -+ return -EINVAL; -+ } -+ - mutex_lock(&data->update_lock); - data->pwm_enable[nr] = val; - if (val == off) { -@@ -1807,6 +2068,7 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr, - data->pwm[0][nr] = 255; - nct6775_write_value(data, data->REG_PWM[0][nr], 255); - } -+ pwm_update_registers(data, nr); - reg = nct6775_read_value(data, data->REG_FAN_MODE[nr]); - reg &= 0x0f; - reg |= pwm_enable_to_reg(val) << 4; -@@ -1815,6 +2077,235 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr, - return count; - } - -+static ssize_t -+show_pwm_temp_sel(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int i, src, sel = 0; -+ -+ src = data->pwm_temp_sel[sattr->index]; -+ -+ for (i = 0; i < NUM_TEMP; i++) { -+ if (!(data->have_temp & (1 << i))) -+ continue; -+ if (src == data->temp_src[i]) { -+ sel = i + 1; -+ break; -+ } -+ } -+ -+ return sprintf(buf, "%d\n", sel); -+} -+ -+static ssize_t -+store_pwm_temp_sel(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ unsigned long val; -+ int err, reg, src; -+ -+ err = kstrtoul(buf, 10, &val); -+ if (err < 0) -+ return err; -+ if (val == 0 || val > NUM_TEMP) -+ return -EINVAL; -+ if (!(data->have_temp & (1 << (val - 1))) || !data->temp_src[val - 1]) -+ return -EINVAL; -+ -+ mutex_lock(&data->update_lock); -+ src = data->temp_src[val - 1]; -+ data->pwm_temp_sel[nr] = src; -+ reg = nct6775_read_value(data, data->REG_TEMP_SEL[nr]); -+ reg &= 0xe0; -+ reg |= src; -+ nct6775_write_value(data, data->REG_TEMP_SEL[nr], reg); -+ mutex_unlock(&data->update_lock); -+ -+ return count; -+} -+ -+static ssize_t -+show_target_temp(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ -+ return sprintf(buf, "%d\n", data->target_temp[sattr->index] * 1000); -+} -+ -+static ssize_t -+store_target_temp(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ unsigned long val; -+ int err; -+ -+ err = kstrtoul(buf, 10, &val); -+ if (err < 0) -+ return err; -+ -+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, -+ data->target_temp_mask); -+ -+ mutex_lock(&data->update_lock); -+ data->target_temp[nr] = val; -+ pwm_update_registers(data, nr); -+ mutex_unlock(&data->update_lock); -+ return count; -+} -+ -+static ssize_t -+show_target_speed(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ -+ return sprintf(buf, "%d\n", -+ fan_from_reg16(data->target_speed[nr], -+ data->fan_div[nr])); -+} -+ -+static ssize_t -+store_target_speed(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ unsigned long val; -+ int err; -+ u16 speed; -+ -+ err = kstrtoul(buf, 10, &val); -+ if (err < 0) -+ return err; -+ -+ val = clamp_val(val, 0, 1350000U); -+ speed = fan_to_reg(val, data->fan_div[nr]); -+ -+ mutex_lock(&data->update_lock); -+ data->target_speed[nr] = speed; -+ pwm_update_registers(data, nr); -+ mutex_unlock(&data->update_lock); -+ return count; -+} -+ -+static ssize_t -+show_temp_tolerance(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); -+ int nr = sattr->nr; -+ int index = sattr->index; -+ -+ return sprintf(buf, "%d\n", data->temp_tolerance[index][nr] * 1000); -+} -+ -+static ssize_t -+store_temp_tolerance(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); -+ int nr = sattr->nr; -+ int index = sattr->index; -+ unsigned long val; -+ int err; -+ -+ err = kstrtoul(buf, 10, &val); -+ if (err < 0) -+ return err; -+ -+ /* Limit tolerance as needed */ -+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, data->tolerance_mask); -+ -+ mutex_lock(&data->update_lock); -+ data->temp_tolerance[index][nr] = val; -+ if (index) -+ pwm_update_registers(data, nr); -+ else -+ nct6775_write_value(data, -+ data->REG_CRITICAL_TEMP_TOLERANCE[nr], -+ val); -+ mutex_unlock(&data->update_lock); -+ return count; -+} -+ -+/* -+ * Fan speed tolerance is a tricky beast, since the associated register is -+ * a tick counter, but the value is reported and configured as rpm. -+ * Compute resulting low and high rpm values and report the difference. -+ */ -+static ssize_t -+show_speed_tolerance(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ int low = data->target_speed[nr] - data->target_speed_tolerance[nr]; -+ int high = data->target_speed[nr] + data->target_speed_tolerance[nr]; -+ int tolerance; -+ -+ if (low <= 0) -+ low = 1; -+ if (high > 0xffff) -+ high = 0xffff; -+ if (high < low) -+ high = low; -+ -+ tolerance = (fan_from_reg16(low, data->fan_div[nr]) -+ - fan_from_reg16(high, data->fan_div[nr])) / 2; -+ -+ return sprintf(buf, "%d\n", tolerance); -+} -+ -+static ssize_t -+store_speed_tolerance(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ unsigned long val; -+ int err; -+ int low, high; -+ -+ err = kstrtoul(buf, 10, &val); -+ if (err < 0) -+ return err; -+ -+ high = fan_from_reg16(data->target_speed[nr], -+ data->fan_div[nr]) + val; -+ low = fan_from_reg16(data->target_speed[nr], -+ data->fan_div[nr]) - val; -+ if (low <= 0) -+ low = 1; -+ if (high < low) -+ high = low; -+ -+ val = (fan_to_reg(low, data->fan_div[nr]) - -+ fan_to_reg(high, data->fan_div[nr])) / 2; -+ -+ /* Limit tolerance as needed */ -+ val = clamp_val(val, 0, data->speed_tolerance_limit); -+ -+ mutex_lock(&data->update_lock); -+ data->target_speed_tolerance[nr] = val; -+ pwm_update_registers(data, nr); -+ mutex_unlock(&data->update_lock); -+ return count; -+} -+ - static SENSOR_DEVICE_ATTR_2(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 0); - static SENSOR_DEVICE_ATTR_2(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1, 0); - static SENSOR_DEVICE_ATTR_2(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2, 0); -@@ -1843,6 +2334,88 @@ static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_enable, - static SENSOR_DEVICE_ATTR(pwm5_enable, S_IWUSR | S_IRUGO, show_pwm_enable, - store_pwm_enable, 4); - -+static SENSOR_DEVICE_ATTR(pwm1_temp_sel, S_IWUSR | S_IRUGO, -+ show_pwm_temp_sel, store_pwm_temp_sel, 0); -+static SENSOR_DEVICE_ATTR(pwm2_temp_sel, S_IWUSR | S_IRUGO, -+ show_pwm_temp_sel, store_pwm_temp_sel, 1); -+static SENSOR_DEVICE_ATTR(pwm3_temp_sel, S_IWUSR | S_IRUGO, -+ show_pwm_temp_sel, store_pwm_temp_sel, 2); -+static SENSOR_DEVICE_ATTR(pwm4_temp_sel, S_IWUSR | S_IRUGO, -+ show_pwm_temp_sel, store_pwm_temp_sel, 3); -+static SENSOR_DEVICE_ATTR(pwm5_temp_sel, S_IWUSR | S_IRUGO, -+ show_pwm_temp_sel, store_pwm_temp_sel, 4); -+ -+static SENSOR_DEVICE_ATTR(pwm1_target_temp, S_IWUSR | S_IRUGO, show_target_temp, -+ store_target_temp, 0); -+static SENSOR_DEVICE_ATTR(pwm2_target_temp, S_IWUSR | S_IRUGO, show_target_temp, -+ store_target_temp, 1); -+static SENSOR_DEVICE_ATTR(pwm3_target_temp, S_IWUSR | S_IRUGO, show_target_temp, -+ store_target_temp, 2); -+static SENSOR_DEVICE_ATTR(pwm4_target_temp, S_IWUSR | S_IRUGO, show_target_temp, -+ store_target_temp, 3); -+static SENSOR_DEVICE_ATTR(pwm5_target_temp, S_IWUSR | S_IRUGO, show_target_temp, -+ store_target_temp, 4); -+ -+static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, show_target_speed, -+ store_target_speed, 0); -+static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO, show_target_speed, -+ store_target_speed, 1); -+static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO, show_target_speed, -+ store_target_speed, 2); -+static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO, show_target_speed, -+ store_target_speed, 3); -+static SENSOR_DEVICE_ATTR(fan5_target, S_IWUSR | S_IRUGO, show_target_speed, -+ store_target_speed, 4); -+ -+static SENSOR_DEVICE_ATTR(fan1_tolerance, S_IWUSR | S_IRUGO, -+ show_speed_tolerance, store_speed_tolerance, 0); -+static SENSOR_DEVICE_ATTR(fan2_tolerance, S_IWUSR | S_IRUGO, -+ show_speed_tolerance, store_speed_tolerance, 1); -+static SENSOR_DEVICE_ATTR(fan3_tolerance, S_IWUSR | S_IRUGO, -+ show_speed_tolerance, store_speed_tolerance, 2); -+static SENSOR_DEVICE_ATTR(fan4_tolerance, S_IWUSR | S_IRUGO, -+ show_speed_tolerance, store_speed_tolerance, 3); -+static SENSOR_DEVICE_ATTR(fan5_tolerance, S_IWUSR | S_IRUGO, -+ show_speed_tolerance, store_speed_tolerance, 4); -+ -+/* Smart Fan registers */ -+ -+static ssize_t -+show_fan_time(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); -+ int nr = sattr->nr; -+ int index = sattr->index; -+ -+ return sprintf(buf, "%d\n", -+ step_time_from_reg(data->fan_time[index][nr], -+ data->pwm_mode[nr])); -+} -+ -+static ssize_t -+store_fan_time(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); -+ int nr = sattr->nr; -+ int index = sattr->index; -+ unsigned long val; -+ int err; -+ -+ err = kstrtoul(buf, 10, &val); -+ if (err < 0) -+ return err; -+ -+ val = step_time_to_reg(val, data->pwm_mode[nr]); -+ mutex_lock(&data->update_lock); -+ data->fan_time[index][nr] = val; -+ nct6775_write_value(data, data->REG_FAN_TIME[index][nr], val); -+ mutex_unlock(&data->update_lock); -+ return count; -+} -+ - static ssize_t - show_name(struct device *dev, struct device_attribute *attr, char *buf) - { -@@ -1853,35 +2426,190 @@ show_name(struct device *dev, struct device_attribute *attr, char *buf) - - static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); - --static struct attribute *nct6775_attributes_pwm[5][4] = { -+static SENSOR_DEVICE_ATTR_2(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_time, -+ store_fan_time, 0, 0); -+static SENSOR_DEVICE_ATTR_2(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_time, -+ store_fan_time, 1, 0); -+static SENSOR_DEVICE_ATTR_2(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_time, -+ store_fan_time, 2, 0); -+static SENSOR_DEVICE_ATTR_2(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_time, -+ store_fan_time, 3, 0); -+static SENSOR_DEVICE_ATTR_2(pwm5_stop_time, S_IWUSR | S_IRUGO, show_fan_time, -+ store_fan_time, 4, 0); -+ -+static SENSOR_DEVICE_ATTR_2(pwm1_step_up_time, S_IWUSR | S_IRUGO, show_fan_time, -+ store_fan_time, 0, 1); -+static SENSOR_DEVICE_ATTR_2(pwm2_step_up_time, S_IWUSR | S_IRUGO, show_fan_time, -+ store_fan_time, 1, 1); -+static SENSOR_DEVICE_ATTR_2(pwm3_step_up_time, S_IWUSR | S_IRUGO, show_fan_time, -+ store_fan_time, 2, 1); -+static SENSOR_DEVICE_ATTR_2(pwm4_step_up_time, S_IWUSR | S_IRUGO, show_fan_time, -+ store_fan_time, 3, 1); -+static SENSOR_DEVICE_ATTR_2(pwm5_step_up_time, S_IWUSR | S_IRUGO, show_fan_time, -+ store_fan_time, 4, 1); -+ -+static SENSOR_DEVICE_ATTR_2(pwm1_step_down_time, S_IWUSR | S_IRUGO, -+ show_fan_time, store_fan_time, 0, 2); -+static SENSOR_DEVICE_ATTR_2(pwm2_step_down_time, S_IWUSR | S_IRUGO, -+ show_fan_time, store_fan_time, 1, 2); -+static SENSOR_DEVICE_ATTR_2(pwm3_step_down_time, S_IWUSR | S_IRUGO, -+ show_fan_time, store_fan_time, 2, 2); -+static SENSOR_DEVICE_ATTR_2(pwm4_step_down_time, S_IWUSR | S_IRUGO, -+ show_fan_time, store_fan_time, 3, 2); -+static SENSOR_DEVICE_ATTR_2(pwm5_step_down_time, S_IWUSR | S_IRUGO, -+ show_fan_time, store_fan_time, 4, 2); -+ -+static SENSOR_DEVICE_ATTR_2(pwm1_start, S_IWUSR | S_IRUGO, show_pwm, -+ store_pwm, 0, 1); -+static SENSOR_DEVICE_ATTR_2(pwm2_start, S_IWUSR | S_IRUGO, show_pwm, -+ store_pwm, 1, 1); -+static SENSOR_DEVICE_ATTR_2(pwm3_start, S_IWUSR | S_IRUGO, show_pwm, -+ store_pwm, 2, 1); -+static SENSOR_DEVICE_ATTR_2(pwm4_start, S_IWUSR | S_IRUGO, show_pwm, -+ store_pwm, 3, 1); -+static SENSOR_DEVICE_ATTR_2(pwm5_start, S_IWUSR | S_IRUGO, show_pwm, -+ store_pwm, 4, 1); -+ -+static SENSOR_DEVICE_ATTR_2(pwm1_floor, S_IWUSR | S_IRUGO, show_pwm, -+ store_pwm, 0, 2); -+static SENSOR_DEVICE_ATTR_2(pwm2_floor, S_IWUSR | S_IRUGO, show_pwm, -+ store_pwm, 1, 2); -+static SENSOR_DEVICE_ATTR_2(pwm3_floor, S_IWUSR | S_IRUGO, show_pwm, -+ store_pwm, 2, 2); -+static SENSOR_DEVICE_ATTR_2(pwm4_floor, S_IWUSR | S_IRUGO, show_pwm, -+ store_pwm, 3, 2); -+static SENSOR_DEVICE_ATTR_2(pwm5_floor, S_IWUSR | S_IRUGO, show_pwm, -+ store_pwm, 4, 2); -+ -+static SENSOR_DEVICE_ATTR_2(pwm1_temp_tolerance, S_IWUSR | S_IRUGO, -+ show_temp_tolerance, store_temp_tolerance, 0, 0); -+static SENSOR_DEVICE_ATTR_2(pwm2_temp_tolerance, S_IWUSR | S_IRUGO, -+ show_temp_tolerance, store_temp_tolerance, 1, 0); -+static SENSOR_DEVICE_ATTR_2(pwm3_temp_tolerance, S_IWUSR | S_IRUGO, -+ show_temp_tolerance, store_temp_tolerance, 2, 0); -+static SENSOR_DEVICE_ATTR_2(pwm4_temp_tolerance, S_IWUSR | S_IRUGO, -+ show_temp_tolerance, store_temp_tolerance, 3, 0); -+static SENSOR_DEVICE_ATTR_2(pwm5_temp_tolerance, S_IWUSR | S_IRUGO, -+ show_temp_tolerance, store_temp_tolerance, 4, 0); -+ -+static SENSOR_DEVICE_ATTR_2(pwm1_crit_temp_tolerance, S_IWUSR | S_IRUGO, -+ show_temp_tolerance, store_temp_tolerance, 0, 1); -+static SENSOR_DEVICE_ATTR_2(pwm2_crit_temp_tolerance, S_IWUSR | S_IRUGO, -+ show_temp_tolerance, store_temp_tolerance, 1, 1); -+static SENSOR_DEVICE_ATTR_2(pwm3_crit_temp_tolerance, S_IWUSR | S_IRUGO, -+ show_temp_tolerance, store_temp_tolerance, 2, 1); -+static SENSOR_DEVICE_ATTR_2(pwm4_crit_temp_tolerance, S_IWUSR | S_IRUGO, -+ show_temp_tolerance, store_temp_tolerance, 3, 1); -+static SENSOR_DEVICE_ATTR_2(pwm5_crit_temp_tolerance, S_IWUSR | S_IRUGO, -+ show_temp_tolerance, store_temp_tolerance, 4, 1); -+ -+/* pwm_max is not supported on all chips */ -+static struct sensor_device_attribute_2 sda_pwm_max[] = { -+ SENSOR_ATTR_2(pwm1_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm, -+ 0, 3), -+ SENSOR_ATTR_2(pwm2_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm, -+ 1, 3), -+ SENSOR_ATTR_2(pwm3_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm, -+ 2, 3), -+ SENSOR_ATTR_2(pwm4_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm, -+ 3, 3), -+ SENSOR_ATTR_2(pwm5_max, S_IWUSR | S_IRUGO, show_pwm, store_pwm, -+ 4, 3), -+}; -+ -+/* pwm_step is not supported on all chips */ -+static struct sensor_device_attribute_2 sda_pwm_step[] = { -+ SENSOR_ATTR_2(pwm1_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0, 4), -+ SENSOR_ATTR_2(pwm2_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1, 4), -+ SENSOR_ATTR_2(pwm3_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2, 4), -+ SENSOR_ATTR_2(pwm4_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3, 4), -+ SENSOR_ATTR_2(pwm5_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 4, 4), -+}; -+ -+static struct attribute *nct6775_attributes_pwm[5][15] = { - { - &sensor_dev_attr_pwm1.dev_attr.attr, - &sensor_dev_attr_pwm1_mode.dev_attr.attr, - &sensor_dev_attr_pwm1_enable.dev_attr.attr, -+ &sensor_dev_attr_pwm1_temp_sel.dev_attr.attr, -+ &sensor_dev_attr_pwm1_temp_tolerance.dev_attr.attr, -+ &sensor_dev_attr_pwm1_crit_temp_tolerance.dev_attr.attr, -+ &sensor_dev_attr_pwm1_target_temp.dev_attr.attr, -+ &sensor_dev_attr_fan1_target.dev_attr.attr, -+ &sensor_dev_attr_fan1_tolerance.dev_attr.attr, -+ &sensor_dev_attr_pwm1_stop_time.dev_attr.attr, -+ &sensor_dev_attr_pwm1_step_up_time.dev_attr.attr, -+ &sensor_dev_attr_pwm1_step_down_time.dev_attr.attr, -+ &sensor_dev_attr_pwm1_start.dev_attr.attr, -+ &sensor_dev_attr_pwm1_floor.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_pwm2.dev_attr.attr, - &sensor_dev_attr_pwm2_mode.dev_attr.attr, - &sensor_dev_attr_pwm2_enable.dev_attr.attr, -+ &sensor_dev_attr_pwm2_temp_sel.dev_attr.attr, -+ &sensor_dev_attr_pwm2_temp_tolerance.dev_attr.attr, -+ &sensor_dev_attr_pwm2_crit_temp_tolerance.dev_attr.attr, -+ &sensor_dev_attr_pwm2_target_temp.dev_attr.attr, -+ &sensor_dev_attr_fan2_target.dev_attr.attr, -+ &sensor_dev_attr_fan2_tolerance.dev_attr.attr, -+ &sensor_dev_attr_pwm2_stop_time.dev_attr.attr, -+ &sensor_dev_attr_pwm2_step_up_time.dev_attr.attr, -+ &sensor_dev_attr_pwm2_step_down_time.dev_attr.attr, -+ &sensor_dev_attr_pwm2_start.dev_attr.attr, -+ &sensor_dev_attr_pwm2_floor.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_pwm3.dev_attr.attr, - &sensor_dev_attr_pwm3_mode.dev_attr.attr, - &sensor_dev_attr_pwm3_enable.dev_attr.attr, -+ &sensor_dev_attr_pwm3_temp_sel.dev_attr.attr, -+ &sensor_dev_attr_pwm3_temp_tolerance.dev_attr.attr, -+ &sensor_dev_attr_pwm3_crit_temp_tolerance.dev_attr.attr, -+ &sensor_dev_attr_pwm3_target_temp.dev_attr.attr, -+ &sensor_dev_attr_fan3_target.dev_attr.attr, -+ &sensor_dev_attr_fan3_tolerance.dev_attr.attr, -+ &sensor_dev_attr_pwm3_stop_time.dev_attr.attr, -+ &sensor_dev_attr_pwm3_step_up_time.dev_attr.attr, -+ &sensor_dev_attr_pwm3_step_down_time.dev_attr.attr, -+ &sensor_dev_attr_pwm3_start.dev_attr.attr, -+ &sensor_dev_attr_pwm3_floor.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_pwm4.dev_attr.attr, - &sensor_dev_attr_pwm4_mode.dev_attr.attr, - &sensor_dev_attr_pwm4_enable.dev_attr.attr, -+ &sensor_dev_attr_pwm4_temp_sel.dev_attr.attr, -+ &sensor_dev_attr_pwm4_temp_tolerance.dev_attr.attr, -+ &sensor_dev_attr_pwm4_crit_temp_tolerance.dev_attr.attr, -+ &sensor_dev_attr_pwm4_target_temp.dev_attr.attr, -+ &sensor_dev_attr_fan4_target.dev_attr.attr, -+ &sensor_dev_attr_fan4_tolerance.dev_attr.attr, -+ &sensor_dev_attr_pwm4_stop_time.dev_attr.attr, -+ &sensor_dev_attr_pwm4_step_up_time.dev_attr.attr, -+ &sensor_dev_attr_pwm4_step_down_time.dev_attr.attr, -+ &sensor_dev_attr_pwm4_start.dev_attr.attr, -+ &sensor_dev_attr_pwm4_floor.dev_attr.attr, - NULL - }, - { - &sensor_dev_attr_pwm5.dev_attr.attr, - &sensor_dev_attr_pwm5_mode.dev_attr.attr, - &sensor_dev_attr_pwm5_enable.dev_attr.attr, -+ &sensor_dev_attr_pwm5_temp_sel.dev_attr.attr, -+ &sensor_dev_attr_pwm5_temp_tolerance.dev_attr.attr, -+ &sensor_dev_attr_pwm5_crit_temp_tolerance.dev_attr.attr, -+ &sensor_dev_attr_pwm5_target_temp.dev_attr.attr, -+ &sensor_dev_attr_fan5_target.dev_attr.attr, -+ &sensor_dev_attr_fan5_tolerance.dev_attr.attr, -+ &sensor_dev_attr_pwm5_stop_time.dev_attr.attr, -+ &sensor_dev_attr_pwm5_step_up_time.dev_attr.attr, -+ &sensor_dev_attr_pwm5_step_down_time.dev_attr.attr, -+ &sensor_dev_attr_pwm5_start.dev_attr.attr, -+ &sensor_dev_attr_pwm5_floor.dev_attr.attr, - NULL - }, - }; -@@ -1895,6 +2623,277 @@ static const struct attribute_group nct6775_group_pwm[5] = { - }; - - static ssize_t -+show_auto_pwm(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); -+ -+ return sprintf(buf, "%d\n", data->auto_pwm[sattr->nr][sattr->index]); -+} -+ -+static ssize_t -+store_auto_pwm(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); -+ int nr = sattr->nr; -+ int point = sattr->index; -+ unsigned long val; -+ int err; -+ u8 reg; -+ -+ err = kstrtoul(buf, 10, &val); -+ if (err < 0) -+ return err; -+ if (val > 255) -+ return -EINVAL; -+ -+ if (point == data->auto_pwm_num) { -+ if (data->kind != nct6775 && !val) -+ return -EINVAL; -+ if (data->kind != nct6779 && val) -+ val = 0xff; -+ } -+ -+ mutex_lock(&data->update_lock); -+ data->auto_pwm[nr][point] = val; -+ if (point < data->auto_pwm_num) { -+ nct6775_write_value(data, -+ NCT6775_AUTO_PWM(data, nr, point), -+ data->auto_pwm[nr][point]); -+ } else { -+ switch (data->kind) { -+ case nct6775: -+ /* disable if needed (pwm == 0) */ -+ reg = nct6775_read_value(data, -+ NCT6775_REG_CRITICAL_ENAB[nr]); -+ if (val) -+ reg |= 0x02; -+ else -+ reg &= ~0x02; -+ nct6775_write_value(data, NCT6775_REG_CRITICAL_ENAB[nr], -+ reg); -+ break; -+ case nct6776: -+ break; /* always enabled, nothing to do */ -+ case nct6779: -+ nct6775_write_value(data, NCT6779_REG_CRITICAL_PWM[nr], -+ val); -+ reg = nct6775_read_value(data, -+ NCT6779_REG_CRITICAL_PWM_ENABLE[nr]); -+ if (val == 255) -+ reg &= ~0x01; -+ else -+ reg |= 0x01; -+ nct6775_write_value(data, -+ NCT6779_REG_CRITICAL_PWM_ENABLE[nr], -+ reg); -+ break; -+ } -+ } -+ mutex_unlock(&data->update_lock); -+ return count; -+} -+ -+static ssize_t -+show_auto_temp(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); -+ int nr = sattr->nr; -+ int point = sattr->index; -+ -+ /* -+ * We don't know for sure if the temperature is signed or unsigned. -+ * Assume it is unsigned. -+ */ -+ return sprintf(buf, "%d\n", data->auto_temp[nr][point] * 1000); -+} -+ -+static ssize_t -+store_auto_temp(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); -+ int nr = sattr->nr; -+ int point = sattr->index; -+ unsigned long val; -+ int err; -+ -+ err = kstrtoul(buf, 10, &val); -+ if (err) -+ return err; -+ if (val > 255000) -+ return -EINVAL; -+ -+ mutex_lock(&data->update_lock); -+ data->auto_temp[nr][point] = DIV_ROUND_CLOSEST(val, 1000); -+ if (point < data->auto_pwm_num) { -+ nct6775_write_value(data, -+ NCT6775_AUTO_TEMP(data, nr, point), -+ data->auto_temp[nr][point]); -+ } else { -+ nct6775_write_value(data, data->REG_CRITICAL_TEMP[nr], -+ data->auto_temp[nr][point]); -+ } -+ mutex_unlock(&data->update_lock); -+ return count; -+} -+ -+/* -+ * The number of auto-point trip points is chip dependent. -+ * Need to check support while generating/removing attribute files. -+ */ -+static struct sensor_device_attribute_2 sda_auto_pwm_arrays[] = { -+ SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 0, 0), -+ SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 0, 0), -+ SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 0, 1), -+ SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 0, 1), -+ SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 0, 2), -+ SENSOR_ATTR_2(pwm1_auto_point3_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 0, 2), -+ SENSOR_ATTR_2(pwm1_auto_point4_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 0, 3), -+ SENSOR_ATTR_2(pwm1_auto_point4_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 0, 3), -+ SENSOR_ATTR_2(pwm1_auto_point5_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 0, 4), -+ SENSOR_ATTR_2(pwm1_auto_point5_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 0, 4), -+ SENSOR_ATTR_2(pwm1_auto_point6_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 0, 5), -+ SENSOR_ATTR_2(pwm1_auto_point6_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 0, 5), -+ SENSOR_ATTR_2(pwm1_auto_point7_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 0, 6), -+ SENSOR_ATTR_2(pwm1_auto_point7_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 0, 6), -+ -+ SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 1, 0), -+ SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 1, 0), -+ SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 1, 1), -+ SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 1, 1), -+ SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 1, 2), -+ SENSOR_ATTR_2(pwm2_auto_point3_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 1, 2), -+ SENSOR_ATTR_2(pwm2_auto_point4_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 1, 3), -+ SENSOR_ATTR_2(pwm2_auto_point4_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 1, 3), -+ SENSOR_ATTR_2(pwm2_auto_point5_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 1, 4), -+ SENSOR_ATTR_2(pwm2_auto_point5_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 1, 4), -+ SENSOR_ATTR_2(pwm2_auto_point6_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 1, 5), -+ SENSOR_ATTR_2(pwm2_auto_point6_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 1, 5), -+ SENSOR_ATTR_2(pwm2_auto_point7_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 1, 6), -+ SENSOR_ATTR_2(pwm2_auto_point7_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 1, 6), -+ -+ SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 2, 0), -+ SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 2, 0), -+ SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 2, 1), -+ SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 2, 1), -+ SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 2, 2), -+ SENSOR_ATTR_2(pwm3_auto_point3_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 2, 2), -+ SENSOR_ATTR_2(pwm3_auto_point4_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 2, 3), -+ SENSOR_ATTR_2(pwm3_auto_point4_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 2, 3), -+ SENSOR_ATTR_2(pwm3_auto_point5_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 2, 4), -+ SENSOR_ATTR_2(pwm3_auto_point5_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 2, 4), -+ SENSOR_ATTR_2(pwm3_auto_point6_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 2, 5), -+ SENSOR_ATTR_2(pwm3_auto_point6_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 2, 5), -+ SENSOR_ATTR_2(pwm3_auto_point7_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 2, 6), -+ SENSOR_ATTR_2(pwm3_auto_point7_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 2, 6), -+ -+ SENSOR_ATTR_2(pwm4_auto_point1_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 3, 0), -+ SENSOR_ATTR_2(pwm4_auto_point1_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 3, 0), -+ SENSOR_ATTR_2(pwm4_auto_point2_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 3, 1), -+ SENSOR_ATTR_2(pwm4_auto_point2_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 3, 1), -+ SENSOR_ATTR_2(pwm4_auto_point3_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 3, 2), -+ SENSOR_ATTR_2(pwm4_auto_point3_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 3, 2), -+ SENSOR_ATTR_2(pwm4_auto_point4_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 3, 3), -+ SENSOR_ATTR_2(pwm4_auto_point4_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 3, 3), -+ SENSOR_ATTR_2(pwm4_auto_point5_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 3, 4), -+ SENSOR_ATTR_2(pwm4_auto_point5_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 3, 4), -+ SENSOR_ATTR_2(pwm4_auto_point6_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 3, 5), -+ SENSOR_ATTR_2(pwm4_auto_point6_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 3, 5), -+ SENSOR_ATTR_2(pwm4_auto_point7_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 3, 6), -+ SENSOR_ATTR_2(pwm4_auto_point7_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 3, 6), -+ -+ SENSOR_ATTR_2(pwm5_auto_point1_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 4, 0), -+ SENSOR_ATTR_2(pwm5_auto_point1_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 4, 0), -+ SENSOR_ATTR_2(pwm5_auto_point2_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 4, 1), -+ SENSOR_ATTR_2(pwm5_auto_point2_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 4, 1), -+ SENSOR_ATTR_2(pwm5_auto_point3_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 4, 2), -+ SENSOR_ATTR_2(pwm5_auto_point3_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 4, 2), -+ SENSOR_ATTR_2(pwm5_auto_point4_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 4, 3), -+ SENSOR_ATTR_2(pwm5_auto_point4_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 4, 3), -+ SENSOR_ATTR_2(pwm5_auto_point5_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 4, 4), -+ SENSOR_ATTR_2(pwm5_auto_point5_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 4, 4), -+ SENSOR_ATTR_2(pwm5_auto_point6_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 4, 5), -+ SENSOR_ATTR_2(pwm5_auto_point6_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 4, 5), -+ SENSOR_ATTR_2(pwm5_auto_point7_pwm, S_IWUSR | S_IRUGO, -+ show_auto_pwm, store_auto_pwm, 4, 6), -+ SENSOR_ATTR_2(pwm5_auto_point7_temp, S_IWUSR | S_IRUGO, -+ show_auto_temp, store_auto_temp, 4, 6), -+}; -+ -+static ssize_t - show_vid(struct device *dev, struct device_attribute *attr, char *buf) - { - struct nct6775_data *data = dev_get_drvdata(dev); -@@ -1969,6 +2968,15 @@ static void nct6775_device_remove_files(struct device *dev) - for (i = 0; i < data->pwm_num; i++) - sysfs_remove_group(&dev->kobj, &nct6775_group_pwm[i]); - -+ for (i = 0; i < ARRAY_SIZE(sda_pwm_max); i++) -+ device_remove_file(dev, &sda_pwm_max[i].dev_attr); -+ -+ for (i = 0; i < ARRAY_SIZE(sda_pwm_step); i++) -+ device_remove_file(dev, &sda_pwm_step[i].dev_attr); -+ -+ for (i = 0; i < ARRAY_SIZE(sda_auto_pwm_arrays); i++) -+ device_remove_file(dev, &sda_auto_pwm_arrays[i].dev_attr); -+ - for (i = 0; i < data->in_num; i++) - sysfs_remove_group(&dev->kobj, &nct6775_group_in[i]); - -@@ -2161,6 +3169,7 @@ static int nct6775_probe(struct platform_device *pdev) - case nct6775: - data->in_num = 9; - data->pwm_num = 3; -+ data->auto_pwm_num = 6; - data->has_fan_div = true; - data->temp_fixed_num = 3; - -@@ -2168,6 +3177,9 @@ static int nct6775_probe(struct platform_device *pdev) - - data->fan_from_reg = fan_from_reg16; - data->fan_from_reg_min = fan_from_reg8; -+ data->target_temp_mask = 0x7f; -+ data->tolerance_mask = 0x0f; -+ data->speed_tolerance_limit = 15; - - data->temp_label = nct6775_temp_label; - data->temp_label_num = ARRAY_SIZE(nct6775_temp_label); -@@ -2178,16 +3190,30 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_VIN = NCT6775_REG_IN; - data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; - data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; -+ data->REG_TARGET = NCT6775_REG_TARGET; - data->REG_FAN = NCT6775_REG_FAN; - data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; - data->REG_FAN_MIN = NCT6775_REG_FAN_MIN; - data->REG_FAN_PULSES = NCT6775_REG_FAN_PULSES; -+ data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; -+ data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; -+ data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; - data->REG_PWM[0] = NCT6775_REG_PWM; -+ data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; -+ data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT; -+ data->REG_PWM[3] = NCT6775_REG_FAN_MAX_OUTPUT; -+ data->REG_PWM[4] = NCT6775_REG_FAN_STEP_OUTPUT; - data->REG_PWM_READ = NCT6775_REG_PWM_READ; - data->REG_PWM_MODE = NCT6775_REG_PWM_MODE; - data->PWM_MODE_MASK = NCT6775_PWM_MODE_MASK; -+ data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP; -+ data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM; -+ data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP; -+ data->REG_CRITICAL_TEMP_TOLERANCE -+ = NCT6775_REG_CRITICAL_TEMP_TOLERANCE; - data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; -+ data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL; - data->REG_ALARM = NCT6775_REG_ALARM; - - reg_temp = NCT6775_REG_TEMP; -@@ -2202,6 +3228,7 @@ static int nct6775_probe(struct platform_device *pdev) - case nct6776: - data->in_num = 9; - data->pwm_num = 3; -+ data->auto_pwm_num = 4; - data->has_fan_div = false; - data->temp_fixed_num = 3; - -@@ -2209,6 +3236,9 @@ static int nct6775_probe(struct platform_device *pdev) - - data->fan_from_reg = fan_from_reg13; - data->fan_from_reg_min = fan_from_reg13; -+ data->target_temp_mask = 0xff; -+ data->tolerance_mask = 0x07; -+ data->speed_tolerance_limit = 63; - - data->temp_label = nct6776_temp_label; - data->temp_label_num = ARRAY_SIZE(nct6776_temp_label); -@@ -2219,16 +3249,29 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_VIN = NCT6775_REG_IN; - data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; - data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; -+ data->REG_TARGET = NCT6775_REG_TARGET; - data->REG_FAN = NCT6775_REG_FAN; - data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; - data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; - data->REG_FAN_PULSES = NCT6776_REG_FAN_PULSES; -+ data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; -+ data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; -+ data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; -+ data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; - data->REG_PWM[0] = NCT6775_REG_PWM; -+ data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; -+ data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT; - data->REG_PWM_READ = NCT6775_REG_PWM_READ; - data->REG_PWM_MODE = NCT6776_REG_PWM_MODE; - data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK; -+ data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP; -+ data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM; -+ data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP; -+ data->REG_CRITICAL_TEMP_TOLERANCE -+ = NCT6775_REG_CRITICAL_TEMP_TOLERANCE; - data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; -+ data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL; - data->REG_ALARM = NCT6775_REG_ALARM; - - reg_temp = NCT6775_REG_TEMP; -@@ -2243,6 +3286,7 @@ static int nct6775_probe(struct platform_device *pdev) - case nct6779: - data->in_num = 15; - data->pwm_num = 5; -+ data->auto_pwm_num = 4; - data->has_fan_div = false; - data->temp_fixed_num = 6; - -@@ -2250,6 +3294,9 @@ static int nct6775_probe(struct platform_device *pdev) - - data->fan_from_reg = fan_from_reg13; - data->fan_from_reg_min = fan_from_reg13; -+ data->target_temp_mask = 0xff; -+ data->tolerance_mask = 0x07; -+ data->speed_tolerance_limit = 63; - - data->temp_label = nct6779_temp_label; - data->temp_label_num = ARRAY_SIZE(nct6779_temp_label); -@@ -2260,16 +3307,29 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_VIN = NCT6779_REG_IN; - data->REG_IN_MINMAX[0] = NCT6775_REG_IN_MIN; - data->REG_IN_MINMAX[1] = NCT6775_REG_IN_MAX; -+ data->REG_TARGET = NCT6775_REG_TARGET; - data->REG_FAN = NCT6779_REG_FAN; - data->REG_FAN_MODE = NCT6775_REG_FAN_MODE; - data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; - data->REG_FAN_PULSES = NCT6779_REG_FAN_PULSES; -+ data->REG_FAN_TIME[0] = NCT6775_REG_FAN_STOP_TIME; -+ data->REG_FAN_TIME[1] = NCT6775_REG_FAN_STEP_UP_TIME; -+ data->REG_FAN_TIME[2] = NCT6775_REG_FAN_STEP_DOWN_TIME; -+ data->REG_TOLERANCE_H = NCT6776_REG_TOLERANCE_H; - data->REG_PWM[0] = NCT6775_REG_PWM; -+ data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; -+ data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT; - data->REG_PWM_READ = NCT6775_REG_PWM_READ; - data->REG_PWM_MODE = NCT6776_REG_PWM_MODE; - data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK; -+ data->REG_AUTO_TEMP = NCT6775_REG_AUTO_TEMP; -+ data->REG_AUTO_PWM = NCT6775_REG_AUTO_PWM; -+ data->REG_CRITICAL_TEMP = NCT6775_REG_CRITICAL_TEMP; -+ data->REG_CRITICAL_TEMP_TOLERANCE -+ = NCT6775_REG_CRITICAL_TEMP_TOLERANCE; - data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; -+ data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL; - data->REG_ALARM = NCT6779_REG_ALARM; - - reg_temp = NCT6779_REG_TEMP; -@@ -2484,6 +3544,31 @@ static int nct6775_probe(struct platform_device *pdev) - err = sysfs_create_group(&dev->kobj, &nct6775_group_pwm[i]); - if (err) - goto exit_remove; -+ -+ if (data->REG_PWM[3]) { -+ err = device_create_file(dev, -+ &sda_pwm_max[i].dev_attr); -+ if (err) -+ goto exit_remove; -+ } -+ if (data->REG_PWM[4]) { -+ err = device_create_file(dev, -+ &sda_pwm_step[i].dev_attr); -+ if (err) -+ goto exit_remove; -+ } -+ } -+ for (i = 0; i < ARRAY_SIZE(sda_auto_pwm_arrays); i++) { -+ struct sensor_device_attribute_2 *attr = -+ &sda_auto_pwm_arrays[i]; -+ -+ if (!(data->has_pwm & (1 << attr->nr))) -+ continue; -+ if (attr->index > data->auto_pwm_num) -+ continue; -+ err = device_create_file(dev, &attr->dev_attr); -+ if (err) -+ goto exit_remove; - } - - for (i = 0; i < data->in_num; i++) { -@@ -2518,7 +3603,7 @@ static int nct6775_probe(struct platform_device *pdev) - goto exit_remove; - } - err = device_create_file(dev, -- &sda_fan_pulses[i].dev_attr); -+ &sda_fan_pulses[i].dev_attr); - if (err) - goto exit_remove; - } - -From bbd8decd4123648ddeba2be485bc7e1a3117bfe4 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Tue, 4 Dec 2012 09:08:29 -0800 -Subject: [PATCH] hwmon: (nct6775) Add support for weighted fan control - -The NCT677X series support weighted fan control. In this mode, a secondary -temperature source is used in addition to the primary temperature source to -control fan speed. Add support for this feature. - -Signed-off-by: Guenter Roeck - -diff --git a/Documentation/hwmon/nct6775 b/Documentation/hwmon/nct6775 -index b4fe6bc..3f5587e 100644 ---- a/Documentation/hwmon/nct6775 -+++ b/Documentation/hwmon/nct6775 -@@ -92,6 +92,25 @@ Common fan control attributes - - pwm[1-5]_temp_sel Temperature source. Value is temperature sensor index. - For example, select '1' for temp1_input. -+pwm[1-5]_weight_temp_sel -+ Secondary temperature source. Value is temperature -+ sensor index. For example, select '1' for temp1_input. -+ Set to 0 to disable secondary temperature control. -+ -+If secondary temperature functionality is enabled, it is controlled with the -+following attributes. -+ -+pwm[1-5]_weight_duty_step -+ Duty step size. -+pwm[1-5]_weight_temp_step -+ Temperature step size. With each step over -+ temp_step_base, the value of weight_duty_step is added -+ to the current pwm value. -+pwm[1-5]_weight_temp_step_base -+ Temperature at which secondary temperature control kicks -+ in. -+pwm[1-5]_weight_temp_step_tol -+ Temperature step tolerance. - - Thermal Cruise mode (2) - ----------------------- -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index 47b1d89..f80ff82 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -255,6 +255,17 @@ static const u16 NCT6775_REG_TEMP_SOURCE[ARRAY_SIZE(NCT6775_REG_TEMP)] = { - static const u16 NCT6775_REG_TEMP_SEL[] = { - 0x100, 0x200, 0x300, 0x800, 0x900 }; - -+static const u16 NCT6775_REG_WEIGHT_TEMP_SEL[] = { -+ 0x139, 0x239, 0x339, 0x839, 0x939 }; -+static const u16 NCT6775_REG_WEIGHT_TEMP_STEP[] = { -+ 0x13a, 0x23a, 0x33a, 0x83a, 0x93a }; -+static const u16 NCT6775_REG_WEIGHT_TEMP_STEP_TOL[] = { -+ 0x13b, 0x23b, 0x33b, 0x83b, 0x93b }; -+static const u16 NCT6775_REG_WEIGHT_DUTY_STEP[] = { -+ 0x13c, 0x23c, 0x33c, 0x83c, 0x93c }; -+static const u16 NCT6775_REG_WEIGHT_TEMP_BASE[] = { -+ 0x13d, 0x23d, 0x33d, 0x83d, 0x93d }; -+ - static const u16 NCT6775_REG_TEMP_OFFSET[] = { 0x454, 0x455, 0x456 }; - - static const u16 NCT6775_REG_AUTO_TEMP[] = { -@@ -323,6 +334,9 @@ static const u8 NCT6776_PWM_MODE_MASK[] = { 0x01, 0, 0 }; - static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642 }; - static const u16 NCT6776_REG_FAN_PULSES[] = { 0x644, 0x645, 0x646, 0, 0 }; - -+static const u16 NCT6776_REG_WEIGHT_DUTY_BASE[] = { -+ 0x13e, 0x23e, 0x33e, 0x83e, 0x93e }; -+ - static const u16 NCT6776_REG_TEMP_CONFIG[ARRAY_SIZE(NCT6775_REG_TEMP)] = { - 0x18, 0x152, 0x252, 0x628, 0x629, 0x62A }; - -@@ -571,8 +585,9 @@ struct nct6775_data { - const u8 *REG_PWM_MODE; - const u8 *PWM_MODE_MASK; - -- const u16 *REG_PWM[5]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor, -- * [3]=pwm_max, [4]=pwm_step -+ const u16 *REG_PWM[7]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor, -+ * [3]=pwm_max, [4]=pwm_step, -+ * [5]=weight_duty_step, [6]=weight_duty_base - */ - const u16 *REG_PWM_READ; - -@@ -584,6 +599,9 @@ struct nct6775_data { - - const u16 *REG_TEMP_SOURCE; /* temp register sources */ - const u16 *REG_TEMP_SEL; -+ const u16 *REG_WEIGHT_TEMP_SEL; -+ const u16 *REG_WEIGHT_TEMP[3]; /* 0=base, 1=tolerance, 2=step */ -+ - const u16 *REG_TEMP_OFFSET; - - const u16 *REG_ALARM; -@@ -625,8 +643,9 @@ struct nct6775_data { - * 4->SmartFan III - * 5->enhanced variable thermal cruise (SmartFan IV) - */ -- u8 pwm[5][5]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor, -- * [3]=pwm_max, [4]=pwm_step -+ u8 pwm[7][5]; /* [0]=pwm, [1]=pwm_start, [2]=pwm_floor, -+ * [3]=pwm_max, [4]=pwm_step, -+ * [5]=weight_duty_step, [6]=weight_duty_base - */ - - u8 target_temp[5]; -@@ -645,6 +664,10 @@ struct nct6775_data { - u8 auto_pwm[5][7]; - u8 auto_temp[5][7]; - u8 pwm_temp_sel[5]; -+ u8 pwm_weight_temp_sel[5]; -+ u8 weight_temp[3][5]; /* 0->temp_step, 1->temp_step_tol, -+ * 2->temp_base -+ */ - - u8 vid; - u8 vrm; -@@ -972,6 +995,19 @@ static void nct6775_update_pwm(struct device *dev) - /* If fan can stop, report floor as 0 */ - if (reg & 0x80) - data->pwm[2][i] = 0; -+ -+ reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[i]); -+ data->pwm_weight_temp_sel[i] = reg & 0x1f; -+ /* If weight is disabled, report weight source as 0 */ -+ if (j == 1 && !(reg & 0x80)) -+ data->pwm_weight_temp_sel[i] = 0; -+ -+ /* Weight temp data */ -+ for (j = 0; j < 3; j++) { -+ data->weight_temp[j][i] -+ = nct6775_read_value(data, -+ data->REG_WEIGHT_TEMP[j][i]); -+ } - } - } - -@@ -1938,9 +1974,9 @@ store_pwm(struct device *dev, struct device_attribute *attr, const char *buf, - int nr = sattr->nr; - int index = sattr->index; - unsigned long val; -- int minval[5] = { 0, 1, 1, data->pwm[2][nr], 0 }; -- int maxval[5] -- = { 255, 255, data->pwm[3][nr] ? : 255, 255, 255 }; -+ int minval[7] = { 0, 1, 1, data->pwm[2][nr], 0, 0, 0 }; -+ int maxval[7] -+ = { 255, 255, data->pwm[3][nr] ? : 255, 255, 255, 255, 255 }; - int err; - u8 reg; - -@@ -2078,13 +2114,9 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr, - } - - static ssize_t --show_pwm_temp_sel(struct device *dev, struct device_attribute *attr, char *buf) -+show_pwm_temp_sel_common(struct nct6775_data *data, char *buf, int src) - { -- struct nct6775_data *data = nct6775_update_device(dev); -- struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -- int i, src, sel = 0; -- -- src = data->pwm_temp_sel[sattr->index]; -+ int i, sel = 0; - - for (i = 0; i < NUM_TEMP; i++) { - if (!(data->have_temp & (1 << i))) -@@ -2099,6 +2131,16 @@ show_pwm_temp_sel(struct device *dev, struct device_attribute *attr, char *buf) - } - - static ssize_t -+show_pwm_temp_sel(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int index = sattr->index; -+ -+ return show_pwm_temp_sel_common(data, buf, data->pwm_temp_sel[index]); -+} -+ -+static ssize_t - store_pwm_temp_sel(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) - { -@@ -2129,6 +2171,56 @@ store_pwm_temp_sel(struct device *dev, struct device_attribute *attr, - } - - static ssize_t -+show_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int index = sattr->index; -+ -+ return show_pwm_temp_sel_common(data, buf, -+ data->pwm_weight_temp_sel[index]); -+} -+ -+static ssize_t -+store_pwm_weight_temp_sel(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); -+ int nr = sattr->index; -+ unsigned long val; -+ int err, reg, src; -+ -+ err = kstrtoul(buf, 10, &val); -+ if (err < 0) -+ return err; -+ if (val > NUM_TEMP) -+ return -EINVAL; -+ if (val && (!(data->have_temp & (1 << (val - 1))) || -+ !data->temp_src[val - 1])) -+ return -EINVAL; -+ -+ mutex_lock(&data->update_lock); -+ if (val) { -+ src = data->temp_src[val - 1]; -+ data->pwm_weight_temp_sel[nr] = src; -+ reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr]); -+ reg &= 0xe0; -+ reg |= (src | 0x80); -+ nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg); -+ } else { -+ data->pwm_weight_temp_sel[nr] = 0; -+ reg = nct6775_read_value(data, data->REG_WEIGHT_TEMP_SEL[nr]); -+ reg &= 0x7f; -+ nct6775_write_value(data, data->REG_WEIGHT_TEMP_SEL[nr], reg); -+ } -+ mutex_unlock(&data->update_lock); -+ -+ return count; -+} -+ -+static ssize_t - show_target_temp(struct device *dev, struct device_attribute *attr, char *buf) - { - struct nct6775_data *data = nct6775_update_device(dev); -@@ -2381,6 +2473,115 @@ static SENSOR_DEVICE_ATTR(fan5_tolerance, S_IWUSR | S_IRUGO, - /* Smart Fan registers */ - - static ssize_t -+show_weight_temp(struct device *dev, struct device_attribute *attr, char *buf) -+{ -+ struct nct6775_data *data = nct6775_update_device(dev); -+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); -+ int nr = sattr->nr; -+ int index = sattr->index; -+ -+ return sprintf(buf, "%d\n", data->weight_temp[index][nr] * 1000); -+} -+ -+static ssize_t -+store_weight_temp(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct nct6775_data *data = dev_get_drvdata(dev); -+ struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); -+ int nr = sattr->nr; -+ int index = sattr->index; -+ unsigned long val; -+ int err; -+ -+ err = kstrtoul(buf, 10, &val); -+ if (err < 0) -+ return err; -+ -+ val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 255); -+ -+ mutex_lock(&data->update_lock); -+ data->weight_temp[index][nr] = val; -+ nct6775_write_value(data, data->REG_WEIGHT_TEMP[index][nr], val); -+ mutex_unlock(&data->update_lock); -+ return count; -+} -+ -+static SENSOR_DEVICE_ATTR(pwm1_weight_temp_sel, S_IWUSR | S_IRUGO, -+ show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, -+ 0); -+static SENSOR_DEVICE_ATTR(pwm2_weight_temp_sel, S_IWUSR | S_IRUGO, -+ show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, -+ 1); -+static SENSOR_DEVICE_ATTR(pwm3_weight_temp_sel, S_IWUSR | S_IRUGO, -+ show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, -+ 2); -+static SENSOR_DEVICE_ATTR(pwm4_weight_temp_sel, S_IWUSR | S_IRUGO, -+ show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, -+ 3); -+static SENSOR_DEVICE_ATTR(pwm5_weight_temp_sel, S_IWUSR | S_IRUGO, -+ show_pwm_weight_temp_sel, store_pwm_weight_temp_sel, -+ 4); -+ -+static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step, S_IWUSR | S_IRUGO, -+ show_weight_temp, store_weight_temp, 0, 0); -+static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step, S_IWUSR | S_IRUGO, -+ show_weight_temp, store_weight_temp, 1, 0); -+static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step, S_IWUSR | S_IRUGO, -+ show_weight_temp, store_weight_temp, 2, 0); -+static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step, S_IWUSR | S_IRUGO, -+ show_weight_temp, store_weight_temp, 3, 0); -+static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step, S_IWUSR | S_IRUGO, -+ show_weight_temp, store_weight_temp, 4, 0); -+ -+static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step_tol, S_IWUSR | S_IRUGO, -+ show_weight_temp, store_weight_temp, 0, 1); -+static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step_tol, S_IWUSR | S_IRUGO, -+ show_weight_temp, store_weight_temp, 1, 1); -+static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step_tol, S_IWUSR | S_IRUGO, -+ show_weight_temp, store_weight_temp, 2, 1); -+static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step_tol, S_IWUSR | S_IRUGO, -+ show_weight_temp, store_weight_temp, 3, 1); -+static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step_tol, S_IWUSR | S_IRUGO, -+ show_weight_temp, store_weight_temp, 4, 1); -+ -+static SENSOR_DEVICE_ATTR_2(pwm1_weight_temp_step_base, S_IWUSR | S_IRUGO, -+ show_weight_temp, store_weight_temp, 0, 2); -+static SENSOR_DEVICE_ATTR_2(pwm2_weight_temp_step_base, S_IWUSR | S_IRUGO, -+ show_weight_temp, store_weight_temp, 1, 2); -+static SENSOR_DEVICE_ATTR_2(pwm3_weight_temp_step_base, S_IWUSR | S_IRUGO, -+ show_weight_temp, store_weight_temp, 2, 2); -+static SENSOR_DEVICE_ATTR_2(pwm4_weight_temp_step_base, S_IWUSR | S_IRUGO, -+ show_weight_temp, store_weight_temp, 3, 2); -+static SENSOR_DEVICE_ATTR_2(pwm5_weight_temp_step_base, S_IWUSR | S_IRUGO, -+ show_weight_temp, store_weight_temp, 4, 2); -+ -+static SENSOR_DEVICE_ATTR_2(pwm1_weight_duty_step, S_IWUSR | S_IRUGO, -+ show_pwm, store_pwm, 0, 5); -+static SENSOR_DEVICE_ATTR_2(pwm2_weight_duty_step, S_IWUSR | S_IRUGO, -+ show_pwm, store_pwm, 1, 5); -+static SENSOR_DEVICE_ATTR_2(pwm3_weight_duty_step, S_IWUSR | S_IRUGO, -+ show_pwm, store_pwm, 2, 5); -+static SENSOR_DEVICE_ATTR_2(pwm4_weight_duty_step, S_IWUSR | S_IRUGO, -+ show_pwm, store_pwm, 3, 5); -+static SENSOR_DEVICE_ATTR_2(pwm5_weight_duty_step, S_IWUSR | S_IRUGO, -+ show_pwm, store_pwm, 4, 5); -+ -+/* duty_base is not supported on all chips */ -+static struct sensor_device_attribute_2 sda_weight_duty_base[] = { -+ SENSOR_ATTR_2(pwm1_weight_duty_base, S_IWUSR | S_IRUGO, -+ show_pwm, store_pwm, 0, 6), -+ SENSOR_ATTR_2(pwm2_weight_duty_base, S_IWUSR | S_IRUGO, -+ show_pwm, store_pwm, 1, 6), -+ SENSOR_ATTR_2(pwm3_weight_duty_base, S_IWUSR | S_IRUGO, -+ show_pwm, store_pwm, 2, 6), -+ SENSOR_ATTR_2(pwm4_weight_duty_base, S_IWUSR | S_IRUGO, -+ show_pwm, store_pwm, 3, 6), -+ SENSOR_ATTR_2(pwm5_weight_duty_base, S_IWUSR | S_IRUGO, -+ show_pwm, store_pwm, 4, 6), -+}; -+ -+static ssize_t - show_fan_time(struct device *dev, struct device_attribute *attr, char *buf) - { - struct nct6775_data *data = nct6775_update_device(dev); -@@ -2526,7 +2727,7 @@ static struct sensor_device_attribute_2 sda_pwm_step[] = { - SENSOR_ATTR_2(pwm5_step, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 4, 4), - }; - --static struct attribute *nct6775_attributes_pwm[5][15] = { -+static struct attribute *nct6775_attributes_pwm[5][20] = { - { - &sensor_dev_attr_pwm1.dev_attr.attr, - &sensor_dev_attr_pwm1_mode.dev_attr.attr, -@@ -2542,6 +2743,11 @@ static struct attribute *nct6775_attributes_pwm[5][15] = { - &sensor_dev_attr_pwm1_step_down_time.dev_attr.attr, - &sensor_dev_attr_pwm1_start.dev_attr.attr, - &sensor_dev_attr_pwm1_floor.dev_attr.attr, -+ &sensor_dev_attr_pwm1_weight_temp_sel.dev_attr.attr, -+ &sensor_dev_attr_pwm1_weight_temp_step.dev_attr.attr, -+ &sensor_dev_attr_pwm1_weight_temp_step_tol.dev_attr.attr, -+ &sensor_dev_attr_pwm1_weight_temp_step_base.dev_attr.attr, -+ &sensor_dev_attr_pwm1_weight_duty_step.dev_attr.attr, - NULL - }, - { -@@ -2559,6 +2765,11 @@ static struct attribute *nct6775_attributes_pwm[5][15] = { - &sensor_dev_attr_pwm2_step_down_time.dev_attr.attr, - &sensor_dev_attr_pwm2_start.dev_attr.attr, - &sensor_dev_attr_pwm2_floor.dev_attr.attr, -+ &sensor_dev_attr_pwm2_weight_temp_sel.dev_attr.attr, -+ &sensor_dev_attr_pwm2_weight_temp_step.dev_attr.attr, -+ &sensor_dev_attr_pwm2_weight_temp_step_tol.dev_attr.attr, -+ &sensor_dev_attr_pwm2_weight_temp_step_base.dev_attr.attr, -+ &sensor_dev_attr_pwm2_weight_duty_step.dev_attr.attr, - NULL - }, - { -@@ -2576,6 +2787,11 @@ static struct attribute *nct6775_attributes_pwm[5][15] = { - &sensor_dev_attr_pwm3_step_down_time.dev_attr.attr, - &sensor_dev_attr_pwm3_start.dev_attr.attr, - &sensor_dev_attr_pwm3_floor.dev_attr.attr, -+ &sensor_dev_attr_pwm3_weight_temp_sel.dev_attr.attr, -+ &sensor_dev_attr_pwm3_weight_temp_step.dev_attr.attr, -+ &sensor_dev_attr_pwm3_weight_temp_step_tol.dev_attr.attr, -+ &sensor_dev_attr_pwm3_weight_temp_step_base.dev_attr.attr, -+ &sensor_dev_attr_pwm3_weight_duty_step.dev_attr.attr, - NULL - }, - { -@@ -2593,6 +2809,11 @@ static struct attribute *nct6775_attributes_pwm[5][15] = { - &sensor_dev_attr_pwm4_step_down_time.dev_attr.attr, - &sensor_dev_attr_pwm4_start.dev_attr.attr, - &sensor_dev_attr_pwm4_floor.dev_attr.attr, -+ &sensor_dev_attr_pwm4_weight_temp_sel.dev_attr.attr, -+ &sensor_dev_attr_pwm4_weight_temp_step.dev_attr.attr, -+ &sensor_dev_attr_pwm4_weight_temp_step_tol.dev_attr.attr, -+ &sensor_dev_attr_pwm4_weight_temp_step_base.dev_attr.attr, -+ &sensor_dev_attr_pwm4_weight_duty_step.dev_attr.attr, - NULL - }, - { -@@ -2610,6 +2831,11 @@ static struct attribute *nct6775_attributes_pwm[5][15] = { - &sensor_dev_attr_pwm5_step_down_time.dev_attr.attr, - &sensor_dev_attr_pwm5_start.dev_attr.attr, - &sensor_dev_attr_pwm5_floor.dev_attr.attr, -+ &sensor_dev_attr_pwm5_weight_temp_sel.dev_attr.attr, -+ &sensor_dev_attr_pwm5_weight_temp_step.dev_attr.attr, -+ &sensor_dev_attr_pwm5_weight_temp_step_tol.dev_attr.attr, -+ &sensor_dev_attr_pwm5_weight_temp_step_base.dev_attr.attr, -+ &sensor_dev_attr_pwm5_weight_duty_step.dev_attr.attr, - NULL - }, - }; -@@ -2974,6 +3200,9 @@ static void nct6775_device_remove_files(struct device *dev) - for (i = 0; i < ARRAY_SIZE(sda_pwm_step); i++) - device_remove_file(dev, &sda_pwm_step[i].dev_attr); - -+ for (i = 0; i < ARRAY_SIZE(sda_weight_duty_base); i++) -+ device_remove_file(dev, &sda_weight_duty_base[i].dev_attr); -+ - for (i = 0; i < ARRAY_SIZE(sda_auto_pwm_arrays); i++) - device_remove_file(dev, &sda_auto_pwm_arrays[i].dev_attr); - -@@ -3203,6 +3432,7 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT; - data->REG_PWM[3] = NCT6775_REG_FAN_MAX_OUTPUT; - data->REG_PWM[4] = NCT6775_REG_FAN_STEP_OUTPUT; -+ data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP; - data->REG_PWM_READ = NCT6775_REG_PWM_READ; - data->REG_PWM_MODE = NCT6775_REG_PWM_MODE; - data->PWM_MODE_MASK = NCT6775_PWM_MODE_MASK; -@@ -3214,6 +3444,10 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; - data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL; -+ data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL; -+ data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP; -+ data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL; -+ data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE; - data->REG_ALARM = NCT6775_REG_ALARM; - - reg_temp = NCT6775_REG_TEMP; -@@ -3261,6 +3495,8 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_PWM[0] = NCT6775_REG_PWM; - data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; - data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT; -+ data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP; -+ data->REG_PWM[6] = NCT6776_REG_WEIGHT_DUTY_BASE; - data->REG_PWM_READ = NCT6775_REG_PWM_READ; - data->REG_PWM_MODE = NCT6776_REG_PWM_MODE; - data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK; -@@ -3272,6 +3508,10 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_TEMP_OFFSET = NCT6775_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; - data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL; -+ data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL; -+ data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP; -+ data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL; -+ data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE; - data->REG_ALARM = NCT6775_REG_ALARM; - - reg_temp = NCT6775_REG_TEMP; -@@ -3319,6 +3559,8 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_PWM[0] = NCT6775_REG_PWM; - data->REG_PWM[1] = NCT6775_REG_FAN_START_OUTPUT; - data->REG_PWM[2] = NCT6775_REG_FAN_STOP_OUTPUT; -+ data->REG_PWM[5] = NCT6775_REG_WEIGHT_DUTY_STEP; -+ data->REG_PWM[6] = NCT6776_REG_WEIGHT_DUTY_BASE; - data->REG_PWM_READ = NCT6775_REG_PWM_READ; - data->REG_PWM_MODE = NCT6776_REG_PWM_MODE; - data->PWM_MODE_MASK = NCT6776_PWM_MODE_MASK; -@@ -3330,6 +3572,10 @@ static int nct6775_probe(struct platform_device *pdev) - data->REG_TEMP_OFFSET = NCT6779_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6775_REG_TEMP_SOURCE; - data->REG_TEMP_SEL = NCT6775_REG_TEMP_SEL; -+ data->REG_WEIGHT_TEMP_SEL = NCT6775_REG_WEIGHT_TEMP_SEL; -+ data->REG_WEIGHT_TEMP[0] = NCT6775_REG_WEIGHT_TEMP_STEP; -+ data->REG_WEIGHT_TEMP[1] = NCT6775_REG_WEIGHT_TEMP_STEP_TOL; -+ data->REG_WEIGHT_TEMP[2] = NCT6775_REG_WEIGHT_TEMP_BASE; - data->REG_ALARM = NCT6779_REG_ALARM; - - reg_temp = NCT6779_REG_TEMP; -@@ -3557,6 +3803,12 @@ static int nct6775_probe(struct platform_device *pdev) - if (err) - goto exit_remove; - } -+ if (data->REG_PWM[6]) { -+ err = device_create_file(dev, -+ &sda_weight_duty_base[i].dev_attr); -+ if (err) -+ goto exit_remove; -+ } - } - for (i = 0; i < ARRAY_SIZE(sda_auto_pwm_arrays); i++) { - struct sensor_device_attribute_2 *attr = - -From 8e9285b0bb2ab48924032147baa29699c0bbee7c Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Tue, 4 Dec 2012 08:03:37 -0800 -Subject: [PATCH] hwmon: (nct6775) Detect and report additional temperature - sources - -Scan all temperature sources used for fan control and report if additional -monitoring registers are available. - -Signed-off-by: Guenter Roeck - -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index f80ff82..6d58597 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -3364,6 +3364,32 @@ nct6775_check_fan_inputs(const struct nct6775_sio_data *sio_data, - return 0; - } - -+static void add_temp_sensors(struct nct6775_data *data, const u16 *regp, -+ int *available, int *mask) -+{ -+ int i; -+ u8 src; -+ -+ for (i = 0; i < data->pwm_num && *available; i++) { -+ int index; -+ -+ if (!regp[i]) -+ continue; -+ src = nct6775_read_value(data, regp[i]); -+ src &= 0x1f; -+ if (!src || (*mask & (1 << src))) -+ continue; -+ if (src >= data->temp_label_num || -+ !strlen(data->temp_label[src])) -+ continue; -+ -+ index = __ffs(*available); -+ nct6775_write_value(data, data->REG_TEMP_SOURCE[index], src); -+ *available &= ~(1 << index); -+ *mask |= 1 << src; -+ } -+} -+ - static int nct6775_probe(struct platform_device *pdev) - { - struct device *dev = &pdev->dev; -@@ -3614,6 +3640,13 @@ static int nct6775_probe(struct platform_device *pdev) - mask |= 1 << src; - } - -+ /* -+ * Now find unmonitored temperature registers and enable monitoring -+ * if additional monitoring registers are available. -+ */ -+ add_temp_sensors(data, data->REG_TEMP_SEL, &available, &mask); -+ add_temp_sensors(data, data->REG_WEIGHT_TEMP_SEL, &available, &mask); -+ - mask = 0; - s = NUM_TEMP_FIXED; /* First dynamic temperature attribute */ - for (i = 0; i < num_reg_temp; i++) { - -From 0fc1f8fc614ca0fef78011b34ef8da638eb9acea Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Tue, 26 Feb 2013 09:43:50 -0800 -Subject: [PATCH] hwmon: (nct6775) Only report VID if supported and enabled - -VID is not always enabled (NCT6775, NCT6776) or supported (NCT6779). - -Signed-off-by: Guenter Roeck - -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index 6d58597..752fbd7 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -3401,6 +3401,8 @@ static int nct6775_probe(struct platform_device *pdev) - const u16 *reg_temp, *reg_temp_over, *reg_temp_hyst, *reg_temp_config; - const u16 *reg_temp_alternate, *reg_temp_crit; - int num_reg_temp; -+ bool have_vid = false; -+ u8 cr2a; - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!devm_request_region(&pdev->dev, res->start, IOREGION_LENGTH, -@@ -3769,17 +3771,31 @@ static int nct6775_probe(struct platform_device *pdev) - /* Initialize the chip */ - nct6775_init_device(data); - -- data->vrm = vid_which_vrm(); - err = superio_enter(sio_data->sioreg); - if (err) - return err; - -+ cr2a = superio_inb(sio_data->sioreg, 0x2a); -+ switch (data->kind) { -+ case nct6775: -+ have_vid = (cr2a & 0x40); -+ break; -+ case nct6776: -+ have_vid = (cr2a & 0x60) == 0x40; -+ break; -+ case nct6779: -+ break; -+ } -+ - /* - * Read VID value - * We can get the VID input values directly at logical device D 0xe3. - */ -- superio_select(sio_data->sioreg, NCT6775_LD_VID); -- data->vid = superio_inb(sio_data->sioreg, 0xe3); -+ if (have_vid) { -+ superio_select(sio_data->sioreg, NCT6775_LD_VID); -+ data->vid = superio_inb(sio_data->sioreg, 0xe3); -+ data->vrm = vid_which_vrm(); -+ } - - if (fan_debounce) { - u8 tmp; -@@ -3804,9 +3820,11 @@ static int nct6775_probe(struct platform_device *pdev) - - superio_exit(sio_data->sioreg); - -- err = device_create_file(dev, &dev_attr_cpu0_vid); -- if (err) -- return err; -+ if (have_vid) { -+ err = device_create_file(dev, &dev_attr_cpu0_vid); -+ if (err) -+ return err; -+ } - - err = nct6775_check_fan_inputs(sio_data, data); - if (err) - -From 236d9039480059f97dc9d3cd75e3651582b62997 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Fri, 8 Mar 2013 07:42:00 -0800 -Subject: [PATCH] hwmon: (nct6775) Drop read/write lock - -The read/write lock is acquired for each read/write operation from/to the chip. -This occurs either during initialization, when it is not needed, or during -updates, when the update_lock is held as well, and it is not needed either. - -Signed-off-by: Guenter Roeck - -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index 752fbd7..2269bb2 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -554,7 +554,6 @@ struct nct6775_data { - const char *name; - - struct device *hwmon_dev; -- struct mutex lock; - - u16 reg_temp[4][NUM_TEMP]; /* 0=temp, 1=temp_over, 2=temp_hyst, - * 3=temp_crit -@@ -745,8 +744,6 @@ static u16 nct6775_read_value(struct nct6775_data *data, u16 reg) - { - int res, word_sized = is_word_sized(data, reg); - -- mutex_lock(&data->lock); -- - nct6775_set_bank(data, reg); - outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET); - res = inb_p(data->addr + DATA_REG_OFFSET); -@@ -755,8 +752,6 @@ static u16 nct6775_read_value(struct nct6775_data *data, u16 reg) - data->addr + ADDR_REG_OFFSET); - res = (res << 8) + inb_p(data->addr + DATA_REG_OFFSET); - } -- -- mutex_unlock(&data->lock); - return res; - } - -@@ -764,8 +759,6 @@ static int nct6775_write_value(struct nct6775_data *data, u16 reg, u16 value) - { - int word_sized = is_word_sized(data, reg); - -- mutex_lock(&data->lock); -- - nct6775_set_bank(data, reg); - outb_p(reg & 0xff, data->addr + ADDR_REG_OFFSET); - if (word_sized) { -@@ -774,8 +767,6 @@ static int nct6775_write_value(struct nct6775_data *data, u16 reg, u16 value) - data->addr + ADDR_REG_OFFSET); - } - outb_p(value & 0xff, data->addr + DATA_REG_OFFSET); -- -- mutex_unlock(&data->lock); - return 0; - } - -@@ -3416,7 +3407,6 @@ static int nct6775_probe(struct platform_device *pdev) - - data->kind = sio_data->kind; - data->addr = res->start; -- mutex_init(&data->lock); - mutex_init(&data->update_lock); - data->name = nct6775_device_names[data->kind]; - data->bank = 0xff; /* Force initial bank selection */ - -From 2c7fd30da21bf6bda12d7a0f678e4fd8ed362c96 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Tue, 2 Apr 2013 08:53:19 -0700 -Subject: [PATCH] hwmon: (nct6775) Expand scope of supported chips - -NCT6775, NCT6776, and NCT6779 have a number of variants with the same -chip ID but different chip labels. Add text "or compatible" to the -message displayed when the driver is loaded and rephrase the Kconfig -entry to reflect that it also supports compatible chips. - -Signed-off-by: Guenter Roeck - -diff --git a/Documentation/hwmon/nct6775 b/Documentation/hwmon/nct6775 -index 3f5587e..4e9ef60 100644 ---- a/Documentation/hwmon/nct6775 -+++ b/Documentation/hwmon/nct6775 -@@ -8,15 +8,15 @@ Kernel driver NCT6775 - ===================== - - Supported chips: -- * Nuvoton NCT6775F/W83667HG-I -+ * Nuvoton NCT5572D/NCT6771F/NCT6772F/NCT6775F/W83677HG-I - Prefix: 'nct6775' - Addresses scanned: ISA address retrieved from Super I/O registers - Datasheet: Available from Nuvoton upon request -- * Nuvoton NCT6776F -+ * Nuvoton NCT5577D/NCT6776D/NCT6776F - Prefix: 'nct6776' - Addresses scanned: ISA address retrieved from Super I/O registers - Datasheet: Available from Nuvoton upon request -- * Nuvoton NCT6779D -+ * Nuvoton NCT5532D/NCT6779D - Prefix: 'nct6779' - Addresses scanned: ISA address retrieved from Super I/O registers - Datasheet: Available from Nuvoton upon request -@@ -28,7 +28,7 @@ Description - ----------- - - This driver implements support for the Nuvoton NCT6775F, NCT6776F, and NCT6779D --super I/O chips. -+and compatible super I/O chips. - - The chips support up to 25 temperature monitoring sources. Up to 6 of those are - direct temperature sensor inputs, the others are special sources such as PECI, -diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig -index 4986961..26ebff0 100644 ---- a/drivers/hwmon/Kconfig -+++ b/drivers/hwmon/Kconfig -@@ -908,14 +908,14 @@ config SENSORS_MCP3021 - will be called mcp3021. - - config SENSORS_NCT6775 -- tristate "Nuvoton NCT6775F, NCT6776F, NCT6779D" -+ tristate "Nuvoton NCT6775F and compatibles" - depends on !PPC - select HWMON_VID - help - If you say yes here you get support for the hardware monitoring -- functionality of the Nuvoton NCT6775F, NCT6776F, and NCT6779D -- Super-I/O chips. This driver replaces the w83627ehf driver for -- NCT6775F and NCT6776F. -+ functionality of the Nuvoton NCT6775F, NCT6776F, NCT6779D -+ and compatible Super-I/O chips. This driver replaces the -+ w83627ehf driver for NCT6775F and NCT6776F. - - This driver can also be built as a module. If so, the module - will be called nct6775. -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index 2269bb2..d05a700 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -4072,16 +4072,17 @@ static struct platform_driver nct6775_driver = { - .remove = nct6775_remove, - }; - -+static const char *nct6775_sio_names[] __initconst = { -+ "NCT6775F", -+ "NCT6776D/F", -+ "NCT6779D", -+}; -+ - /* nct6775_find() looks for a '627 in the Super-I/O config space */ - static int __init nct6775_find(int sioaddr, unsigned short *addr, - struct nct6775_sio_data *sio_data) - { -- static const char sio_name_NCT6775[] __initconst = "NCT6775F"; -- static const char sio_name_NCT6776[] __initconst = "NCT6776F"; -- static const char sio_name_NCT6779[] __initconst = "NCT6779D"; -- - u16 val; -- const char *sio_name; - int err; - - err = superio_enter(sioaddr); -@@ -4096,15 +4097,12 @@ static int __init nct6775_find(int sioaddr, unsigned short *addr, - switch (val & SIO_ID_MASK) { - case SIO_NCT6775_ID: - sio_data->kind = nct6775; -- sio_name = sio_name_NCT6775; - break; - case SIO_NCT6776_ID: - sio_data->kind = nct6776; -- sio_name = sio_name_NCT6776; - break; - case SIO_NCT6779_ID: - sio_data->kind = nct6779; -- sio_name = sio_name_NCT6779; - break; - default: - if (val != 0xffff) -@@ -4132,7 +4130,8 @@ static int __init nct6775_find(int sioaddr, unsigned short *addr, - } - - superio_exit(sioaddr); -- pr_info("Found %s chip at %#x\n", sio_name, *addr); -+ pr_info("Found %s or compatible chip at %#x\n", -+ nct6775_sio_names[sio_data->kind], *addr); - sio_data->sioreg = sioaddr; - - return 0; - -From 573728c647fb991ec7be6408a4975413d2ace6c3 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Fri, 5 Apr 2013 23:15:53 -0700 -Subject: [PATCH] hwmon: (nct6775) Enable both AUXTIN and VIN3 on NCT6776 - -Per datasheet, VIN3 and AUXTIN share the same external pin. However, there -is no clean way to detect this condition. Furthermore, both are reported -by the BIOS on Supermicro C7H61. It may thus be possible that chip revisions -exist where both attributes are supported at the same time. -Better play safe and report both. - -Signed-off-by: Guenter Roeck - -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index d05a700..8d0e4c4 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -3721,43 +3721,6 @@ static int nct6775_probe(struct platform_device *pdev) - } - #endif /* USE_ALTERNATE */ - -- switch (data->kind) { -- case nct6775: -- break; -- case nct6776: -- /* -- * On NCT6776, AUXTIN and VIN3 pins are shared. -- * Only way to detect it is to check if AUXTIN is used -- * as a temperature source, and if that source is -- * enabled. -- * -- * If that is the case, disable in6, which reports VIN3. -- * Otherwise disable temp3. -- */ -- if (data->have_temp & (1 << 2)) { -- u8 reg = nct6775_read_value(data, -- data->reg_temp_config[2]); -- if (reg & 0x01) -- data->have_temp &= ~(1 << 2); -- else -- data->have_in &= ~(1 << 6); -- } -- break; -- case nct6779: -- /* -- * Shared pins: -- * VIN4 / AUXTIN0 -- * VIN5 / AUXTIN1 -- * VIN6 / AUXTIN2 -- * VIN7 / AUXTIN3 -- * -- * There does not seem to be a clean way to detect if VINx or -- * AUXTINx is active, so for keep both sensor types enabled -- * for now. -- */ -- break; -- } -- - /* Initialize the chip */ - nct6775_init_device(data); - - -From c409fd43bdea705799d21531600b8f305b120009 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Tue, 9 Apr 2013 05:04:00 -0700 -Subject: [PATCH] hwmon: (nct6775) Use ARRAY_SIZE for loops where possible - -This ensures that the loop iterations are correct even if/when -the number of elements in an array changes. - -Signed-off-by: Guenter Roeck - -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index 8d0e4c4..04ccfff 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -854,7 +854,7 @@ static void nct6775_init_fan_div(struct nct6775_data *data) - * reading from the fan count register, even if it is not optimal. - * We'll compute a better divider later on. - */ -- for (i = 0; i < 3; i++) { -+ for (i = 0; i < ARRAY_SIZE(data->fan_div); i++) { - if (!(data->has_fan & (1 << i))) - continue; - if (data->fan_div[i] == 0) { -@@ -877,7 +877,7 @@ static void nct6775_init_fan_common(struct device *dev, - * If fan_min is not set (0), set it to 0xff to disable it. This - * prevents the unnecessary warning when fanX_min is reported as 0. - */ -- for (i = 0; i < 5; i++) { -+ for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) { - if (data->has_fan_min & (1 << i)) { - reg = nct6775_read_value(data, data->REG_FAN_MIN[i]); - if (!reg) -@@ -994,7 +994,7 @@ static void nct6775_update_pwm(struct device *dev) - data->pwm_weight_temp_sel[i] = 0; - - /* Weight temp data */ -- for (j = 0; j < 3; j++) { -+ for (j = 0; j < ARRAY_SIZE(data->weight_temp); j++) { - data->weight_temp[j][i] - = nct6775_read_value(data, - data->REG_WEIGHT_TEMP[j][i]); -@@ -1013,7 +1013,7 @@ static void nct6775_update_pwm_limits(struct device *dev) - if (!(data->has_pwm & (1 << i))) - continue; - -- for (j = 0; j < 3; j++) { -+ for (j = 0; j < ARRAY_SIZE(data->fan_time); j++) { - data->fan_time[j][i] = - nct6775_read_value(data, data->REG_FAN_TIME[j][i]); - } -@@ -1095,7 +1095,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) - } - - /* Measured fan speeds and limits */ -- for (i = 0; i < 5; i++) { -+ for (i = 0; i < ARRAY_SIZE(data->rpm); i++) { - u16 reg; - - if (!(data->has_fan & (1 << i))) -@@ -1121,7 +1121,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) - for (i = 0; i < NUM_TEMP; i++) { - if (!(data->have_temp & (1 << i))) - continue; -- for (j = 0; j < 4; j++) { -+ for (j = 0; j < ARRAY_SIZE(data->reg_temp); j++) { - if (data->reg_temp[j][i]) - data->temp[j][i] - = nct6775_read_temp(data, -@@ -3983,7 +3983,7 @@ static int nct6775_resume(struct device *dev) - data->in[i][2]); - } - -- for (i = 0; i < 5; i++) { -+ for (i = 0; i < ARRAY_SIZE(data->fan_min); i++) { - if (!(data->has_fan_min & (1 << i))) - continue; - -@@ -3995,7 +3995,7 @@ static int nct6775_resume(struct device *dev) - if (!(data->have_temp & (1 << i))) - continue; - -- for (j = 1; j < 4; j++) -+ for (j = 1; j < ARRAY_SIZE(data->reg_temp); j++) - if (data->reg_temp[j][i]) - nct6775_write_temp(data, data->reg_temp[j][i], - data->temp[j][i]); - -From 6d4b3621bb613da51ae7474c50f5cb37465b6f37 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Sun, 21 Apr 2013 09:08:11 -0700 -Subject: [PATCH] hwmon: (nct6775) Constify strings - -nct6775_sio_names should be a constant pointer to an array of -constant strings. - -Signed-off-by: Guenter Roeck - -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index 04ccfff..14da90e 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -4035,7 +4035,7 @@ static struct platform_driver nct6775_driver = { - .remove = nct6775_remove, - }; - --static const char *nct6775_sio_names[] __initconst = { -+static const char * const nct6775_sio_names[] __initconst = { - "NCT6775F", - "NCT6776D/F", - "NCT6779D", - -From 6445e6600fa632448cac64e71119310378464ad9 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Sun, 21 Apr 2013 09:13:28 -0700 -Subject: [PATCH] hwmon: (nct6775) Fix coding style problems - -Add space around binary operators (CodingStyle, chapter 3.1). - -Signed-off-by: Guenter Roeck - -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index 14da90e..f43f5e5 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -833,7 +833,7 @@ static void nct6775_update_fan_div(struct nct6775_data *data) - data->fan_div[1] = (i & 0x70) >> 4; - i = nct6775_read_value(data, NCT6775_REG_FANDIV2); - data->fan_div[2] = i & 0x7; -- if (data->has_fan & (1<<3)) -+ if (data->has_fan & (1 << 3)) - data->fan_div[3] = (i & 0x70) >> 4; - } - -@@ -1076,7 +1076,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) - - mutex_lock(&data->update_lock); - -- if (time_after(jiffies, data->last_updated + HZ + HZ/2) -+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - /* Fan clock dividers */ - nct6775_update_fan_div_common(data); -@@ -1177,7 +1177,7 @@ store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf, - return err; - mutex_lock(&data->update_lock); - data->in[nr][index] = in_to_reg(val, nr); -- nct6775_write_value(data, data->REG_IN_MINMAX[index-1][nr], -+ nct6775_write_value(data, data->REG_IN_MINMAX[index - 1][nr], - data->in[nr][index]); - mutex_unlock(&data->update_lock); - return count; - -From 169c05cd54473ba4cc37bf4d22e7631395d14f68 Mon Sep 17 00:00:00 2001 -From: Guenter Roeck -Date: Thu, 9 May 2013 10:40:01 -0700 -Subject: [PATCH] hwmon: (nct6775) Do not create non-existing attributes - -Overtemperature and hysteresis registers only exist for primary -temperature registers, not for alternates, so do not assign -those registers when initializing alternates. - -Signed-off-by: Guenter Roeck - -diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c -index f43f5e5..04638ae 100644 ---- a/drivers/hwmon/nct6775.c -+++ b/drivers/hwmon/nct6775.c -@@ -3705,8 +3705,10 @@ static int nct6775_probe(struct platform_device *pdev) - data->have_temp |= 1 << i; - data->have_temp_fixed |= 1 << i; - data->reg_temp[0][i] = reg_temp_alternate[i]; -- data->reg_temp[1][i] = reg_temp_over[i]; -- data->reg_temp[2][i] = reg_temp_hyst[i]; -+ if (i < num_reg_temp) { -+ data->reg_temp[1][i] = reg_temp_over[i]; -+ data->reg_temp[2][i] = reg_temp_hyst[i]; -+ } - data->temp_src[i] = i + 1; - continue; - } diff --git a/drm-i915-correctly-restore-fences-with-objects-attac.patch b/drm-i915-correctly-restore-fences-with-objects-attac.patch new file mode 100644 index 000000000..69bb93fa5 --- /dev/null +++ b/drm-i915-correctly-restore-fences-with-objects-attac.patch @@ -0,0 +1,113 @@ +From 94a335dba34ff47cad3d6d0c29b452d43a1be3c8 Mon Sep 17 00:00:00 2001 +From: Daniel Vetter +Date: Wed, 17 Jul 2013 14:51:28 +0200 +Subject: [PATCH] drm/i915: correctly restore fences with objects attached +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +To avoid stalls we delay tiling changes and especially hold of +committing the new fence state for as long as possible. +Synchronization points are in the execbuf code and in our gtt fault +handler. + +Unfortunately we've missed that tricky detail when adding proper fence +restore code in + +commit 19b2dbde5732170a03bd82cc8bd442cf88d856f7 +Author: Chris Wilson +Date: Wed Jun 12 10:15:12 2013 +0100 + + drm/i915: Restore fences after resume and GPU resets + +The result was that we've restored fences for objects with no tiling, +since the object<->fence link still existed after resume. Now that +wouldn't have been too bad since any subsequent access would have +fixed things up, but if we've changed from tiled to untiled real havoc +happened: + +The tiling stride is stored -1 in the fence register, so a stride of 0 +resulted in all 1s in the top 32bits, and so a completely bogus fence +spanning everything from the start of the object to the top of the +GTT. The tell-tale in the register dumps looks like: + + FENCE START 2: 0x0214d001 + FENCE END 2: 0xfffff3ff + +Bit 11 isn't set since the hw doesn't store it, even when writing all +1s (at least on my snb here). + +To prevent such a gaffle in the future add a sanity check for fences +with an untiled object attached in i915_gem_write_fence. + +v2: Fix the WARN, spotted by Chris. + +v3: Trying to reuse get_fences looked ugly and obfuscated the code. +Instead reuse update_fence and to make it really dtrt also move the +fence dirty state clearing into update_fence. + +Cc: Chris Wilson +Cc: Stéphane Marchesin +Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=60530 +Cc: stable@vger.kernel.org (for 3.10 only) +Reviewed-by: Chris Wilson +Tested-by: Matthew Garrett +Tested-by: Björn Bidar +Signed-off-by: Daniel Vetter +--- + drivers/gpu/drm/i915/i915_gem.c | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c +index 97afd26..d9e2208 100644 +--- a/drivers/gpu/drm/i915/i915_gem.c ++++ b/drivers/gpu/drm/i915/i915_gem.c +@@ -2258,7 +2258,17 @@ void i915_gem_restore_fences(struct drm_device *dev) + + for (i = 0; i < dev_priv->num_fence_regs; i++) { + struct drm_i915_fence_reg *reg = &dev_priv->fence_regs[i]; +- i915_gem_write_fence(dev, i, reg->obj); ++ ++ /* ++ * Commit delayed tiling changes if we have an object still ++ * attached to the fence, otherwise just clear the fence. ++ */ ++ if (reg->obj) { ++ i915_gem_object_update_fence(reg->obj, reg, ++ reg->obj->tiling_mode); ++ } else { ++ i915_gem_write_fence(dev, i, NULL); ++ } + } + } + +@@ -2795,6 +2805,10 @@ static void i915_gem_write_fence(struct drm_device *dev, int reg, + if (i915_gem_object_needs_mb(dev_priv->fence_regs[reg].obj)) + mb(); + ++ WARN(obj && (!obj->stride || !obj->tiling_mode), ++ "bogus fence setup with stride: 0x%x, tiling mode: %i\n", ++ obj->stride, obj->tiling_mode); ++ + switch (INTEL_INFO(dev)->gen) { + case 7: + case 6: +@@ -2836,6 +2850,7 @@ static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj, + fence->obj = NULL; + list_del_init(&fence->lru_list); + } ++ obj->fence_dirty = false; + } + + static int +@@ -2965,7 +2980,6 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj) + return 0; + + i915_gem_object_update_fence(obj, reg, enable); +- obj->fence_dirty = false; + + return 0; + } +-- +1.8.3.1 + diff --git a/drm-i915-dp-stfu.patch b/drm-i915-dp-stfu.patch index 0f28ed240..fb2e58ee9 100644 --- a/drm-i915-dp-stfu.patch +++ b/drm-i915-dp-stfu.patch @@ -1,17 +1,17 @@ diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c -index 296cfc2..516e1e2 100644 +index fb2fbc1..0aaf67d 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c -@@ -350,7 +350,7 @@ intel_dp_check_edp(struct intel_dp *intel_dp) - if (!is_edp(intel_dp)) - return; +@@ -283,7 +283,7 @@ intel_dp_check_edp(struct intel_dp *intel_dp) + pp_ctrl_reg = IS_VALLEYVIEW(dev) ? PIPEA_PP_CONTROL : PCH_PP_CONTROL; + if (!ironlake_edp_have_panel_power(intel_dp) && !ironlake_edp_have_panel_vdd(intel_dp)) { - WARN(1, "eDP powered off while attempting aux channel communication.\n"); + DRM_ERROR("eDP powered off while attempting aux channel communication.\n"); DRM_DEBUG_KMS("Status 0x%08x Control 0x%08x\n", - I915_READ(PCH_PP_STATUS), - I915_READ(PCH_PP_CONTROL)); -@@ -447,7 +447,7 @@ intel_dp_aux_ch(struct intel_dp *intel_d + I915_READ(pp_stat_reg), + I915_READ(pp_ctrl_reg)); +@@ -376,7 +376,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, } if (try == 3) { @@ -20,7 +20,7 @@ index 296cfc2..516e1e2 100644 I915_READ(ch_ctl)); ret = -EBUSY; goto out; -@@ -1024,8 +1024,8 @@ static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp) +@@ -995,8 +995,8 @@ void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp) return; DRM_DEBUG_KMS("Turn eDP VDD on\n"); @@ -31,7 +31,7 @@ index 296cfc2..516e1e2 100644 intel_dp->want_panel_vdd = true; -@@ -1090,7 +1090,8 @@ static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync) +@@ -1070,7 +1070,8 @@ void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync) return; DRM_DEBUG_KMS("Turn eDP VDD off %d\n", intel_dp->want_panel_vdd); @@ -41,7 +41,7 @@ index 296cfc2..516e1e2 100644 intel_dp->want_panel_vdd = false; -@@ -1160,7 +1161,8 @@ static void ironlake_edp_panel_off(struct intel_dp *intel_dp) +@@ -1144,7 +1145,8 @@ void ironlake_edp_panel_off(struct intel_dp *intel_dp) DRM_DEBUG_KMS("Turn eDP power off\n"); @@ -49,5 +49,5 @@ index 296cfc2..516e1e2 100644 + if (!intel_dp->want_panel_vdd) + DRM_ERROR("Need VDD to turn off panel\n"); - pp = ironlake_get_pp_control(dev_priv); - pp &= ~(POWER_TARGET_ON | PANEL_POWER_RESET | EDP_BLC_ENABLE); + pp = ironlake_get_pp_control(intel_dp); + /* We need to switch off panel power _and_ force vdd, for otherwise some diff --git a/i7300_edac_single_mode_fixup.patch b/i7300_edac_single_mode_fixup.patch deleted file mode 100644 index ed08ab961..000000000 --- a/i7300_edac_single_mode_fixup.patch +++ /dev/null @@ -1,108 +0,0 @@ -commit 8ed5b5d41168a98cffa63e2f6c51c3243e159706 -Author: Mauro Carvalho Chehab -Date: Wed Mar 13 22:56:33 2013 -0300 - - i7300_edac: Fix memory detection in single mode - - When the machine is on single mode, only branch 0 channel 0 - is valid. However, the code is not honouring it: - - [ 1952.639341] EDAC DEBUG: i7300_get_mc_regs: Memory controller operating on single mode - ... - [ 1952.639351] EDAC DEBUG: i7300_init_csrows: AMB-present CH0 = 0x1: - [ 1952.639353] EDAC DEBUG: i7300_init_csrows: AMB-present CH1 = 0x0: - [ 1952.639355] EDAC DEBUG: i7300_init_csrows: AMB-present CH2 = 0x0: - [ 1952.639358] EDAC DEBUG: i7300_init_csrows: AMB-present CH3 = 0x0: - ... - [ 1952.639360] EDAC DEBUG: decode_mtr: MTR0 CH0: DIMMs are Present (mtr) - [ 1952.639362] EDAC DEBUG: decode_mtr: WIDTH: x8 - [ 1952.639363] EDAC DEBUG: decode_mtr: ELECTRICAL THROTTLING is enabled - [ 1952.639364] EDAC DEBUG: decode_mtr: NUMBANK: 4 bank(s) - [ 1952.639366] EDAC DEBUG: decode_mtr: NUMRANK: single - [ 1952.639367] EDAC DEBUG: decode_mtr: NUMROW: 16,384 - 14 rows - [ 1952.639368] EDAC DEBUG: decode_mtr: NUMCOL: 1,024 - 10 columns - [ 1952.639370] EDAC DEBUG: decode_mtr: SIZE: 512 MB - [ 1952.639371] EDAC DEBUG: decode_mtr: ECC code is 8-byte-over-32-byte SECDED+ code - [ 1952.639373] EDAC DEBUG: decode_mtr: Scrub algorithm for x8 is on enhanced mode - [ 1952.639374] EDAC DEBUG: decode_mtr: MTR0 CH1: DIMMs are Present (mtr) - [ 1952.639376] EDAC DEBUG: decode_mtr: WIDTH: x8 - [ 1952.639377] EDAC DEBUG: decode_mtr: ELECTRICAL THROTTLING is enabled - [ 1952.639379] EDAC DEBUG: decode_mtr: NUMBANK: 4 bank(s) - [ 1952.639380] EDAC DEBUG: decode_mtr: NUMRANK: single - [ 1952.639381] EDAC DEBUG: decode_mtr: NUMROW: 16,384 - 14 rows - [ 1952.639383] EDAC DEBUG: decode_mtr: NUMCOL: 1,024 - 10 columns - [ 1952.639384] EDAC DEBUG: decode_mtr: SIZE: 512 MB - [ 1952.639385] EDAC DEBUG: decode_mtr: ECC code is 8-byte-over-32-byte SECDED+ code - [ 1952.639387] EDAC DEBUG: decode_mtr: Scrub algorithm for x8 is on enhanced mode - ... - [ 1952.639449] EDAC DEBUG: print_dimm_size: channel 0 | channel 1 | channel 2 | channel 3 | - [ 1952.639451] EDAC DEBUG: print_dimm_size: ------------------------------------------------------------- - [ 1952.639453] EDAC DEBUG: print_dimm_size: csrow/SLOT 0 512 MB | 512 MB | 0 MB | 0 MB | - [ 1952.639456] EDAC DEBUG: print_dimm_size: csrow/SLOT 1 0 MB | 0 MB | 0 MB | 0 MB | - [ 1952.639458] EDAC DEBUG: print_dimm_size: csrow/SLOT 2 0 MB | 0 MB | 0 MB | 0 MB | - [ 1952.639460] EDAC DEBUG: print_dimm_size: csrow/SLOT 3 0 MB | 0 MB | 0 MB | 0 MB | - [ 1952.639462] EDAC DEBUG: print_dimm_size: csrow/SLOT 4 0 MB | 0 MB | 0 MB | 0 MB | - [ 1952.639464] EDAC DEBUG: print_dimm_size: csrow/SLOT 5 0 MB | 0 MB | 0 MB | 0 MB | - [ 1952.639466] EDAC DEBUG: print_dimm_size: csrow/SLOT 6 0 MB | 0 MB | 0 MB | 0 MB | - [ 1952.639468] EDAC DEBUG: print_dimm_size: csrow/SLOT 7 0 MB | 0 MB | 0 MB | 0 MB | - [ 1952.639470] EDAC DEBUG: print_dimm_size: ------------------------------------------------------------- - - Instead of detecting a single memory at channel 0, it is showing - twice the memory. - - Signed-off-by: Mauro Carvalho Chehab - -diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c -index 087c27b..9004c64 100644 ---- a/drivers/edac/i7300_edac.c -+++ b/drivers/edac/i7300_edac.c -@@ -750,15 +750,23 @@ static int i7300_init_csrows(struct mem_ctl_info *mci) - struct i7300_dimm_info *dinfo; - int rc = -ENODEV; - int mtr; -- int ch, branch, slot, channel; -+ int ch, branch, slot, channel, max_channel, max_branch; - struct dimm_info *dimm; - - pvt = mci->pvt_info; - - edac_dbg(2, "Memory Technology Registers:\n"); - -+ if (IS_SINGLE_MODE(pvt->mc_settings_a)) { -+ max_branch = 1; -+ max_channel = 1; -+ } else { -+ max_branch = MAX_BRANCHES; -+ max_channel = MAX_CH_PER_BRANCH; -+ } -+ - /* Get the AMB present registers for the four channels */ -- for (branch = 0; branch < MAX_BRANCHES; branch++) { -+ for (branch = 0; branch < max_branch; branch++) { - /* Read and dump branch 0's MTRs */ - channel = to_channel(0, branch); - pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch], -@@ -767,6 +775,9 @@ static int i7300_init_csrows(struct mem_ctl_info *mci) - edac_dbg(2, "\t\tAMB-present CH%d = 0x%x:\n", - channel, pvt->ambpresent[channel]); - -+ if (max_channel == 1) -+ continue; -+ - channel = to_channel(1, branch); - pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch], - AMBPRESENT_1, -@@ -778,11 +789,11 @@ static int i7300_init_csrows(struct mem_ctl_info *mci) - /* Get the set of MTR[0-7] regs by each branch */ - for (slot = 0; slot < MAX_SLOTS; slot++) { - int where = mtr_regs[slot]; -- for (branch = 0; branch < MAX_BRANCHES; branch++) { -+ for (branch = 0; branch < max_branch; branch++) { - pci_read_config_word(pvt->pci_dev_2x_0_fbd_branch[branch], - where, - &pvt->mtr[slot][branch]); -- for (ch = 0; ch < MAX_CH_PER_BRANCH; ch++) { -+ for (ch = 0; ch < max_channel; ch++) { - int channel = to_channel(ch, branch); - - dimm = EDAC_DIMM_PTR(mci->layers, mci->dimms, diff --git a/ipv6-call-udp_push_pending_frames-when-uncorking-a-socket-with-AF_INET-pending-data.patch b/ipv6-call-udp_push_pending_frames-when-uncorking-a-socket-with-AF_INET-pending-data.patch deleted file mode 100644 index 8f37348e9..000000000 --- a/ipv6-call-udp_push_pending_frames-when-uncorking-a-socket-with-AF_INET-pending-data.patch +++ /dev/null @@ -1,128 +0,0 @@ -From 0e3f585c132e7716b8b96c20c59b15a24ec2790e Mon Sep 17 00:00:00 2001 -From: Hannes Frederic Sowa -Date: Mon, 1 Jul 2013 20:21:30 +0200 -Subject: [PATCH 11/40] ipv6: call udp_push_pending_frames when uncorking a - socket with AF_INET pending data - -[ Upstream commit 8822b64a0fa64a5dd1dfcf837c5b0be83f8c05d1 ] - -We accidentally call down to ip6_push_pending_frames when uncorking -pending AF_INET data on a ipv6 socket. This results in the following -splat (from Dave Jones): - -skbuff: skb_under_panic: text:ffffffff816765f6 len:48 put:40 head:ffff88013deb6df0 data:ffff88013deb6dec tail:0x2c end:0xc0 dev: -------------[ cut here ]------------ -kernel BUG at net/core/skbuff.c:126! -invalid opcode: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC -Modules linked in: dccp_ipv4 dccp 8021q garp bridge stp dlci mpoa snd_seq_dummy sctp fuse hidp tun bnep nfnetlink scsi_transport_iscsi rfcomm can_raw can_bcm af_802154 appletalk caif_socket can caif ipt_ULOG x25 rose af_key pppoe pppox ipx phonet irda llc2 ppp_generic slhc p8023 psnap p8022 llc crc_ccitt atm bluetooth -+netrom ax25 nfc rfkill rds af_rxrpc coretemp hwmon kvm_intel kvm crc32c_intel snd_hda_codec_realtek ghash_clmulni_intel microcode pcspkr snd_hda_codec_hdmi snd_hda_intel snd_hda_codec snd_hwdep usb_debug snd_seq snd_seq_device snd_pcm e1000e snd_page_alloc snd_timer ptp snd pps_core soundcore xfs libcrc32c -CPU: 2 PID: 8095 Comm: trinity-child2 Not tainted 3.10.0-rc7+ #37 -task: ffff8801f52c2520 ti: ffff8801e6430000 task.ti: ffff8801e6430000 -RIP: 0010:[] [] skb_panic+0x63/0x65 -RSP: 0018:ffff8801e6431de8 EFLAGS: 00010282 -RAX: 0000000000000086 RBX: ffff8802353d3cc0 RCX: 0000000000000006 -RDX: 0000000000003b90 RSI: ffff8801f52c2ca0 RDI: ffff8801f52c2520 -RBP: ffff8801e6431e08 R08: 0000000000000000 R09: 0000000000000000 -R10: 0000000000000001 R11: 0000000000000001 R12: ffff88022ea0c800 -R13: ffff88022ea0cdf8 R14: ffff8802353ecb40 R15: ffffffff81cc7800 -FS: 00007f5720a10740(0000) GS:ffff880244c00000(0000) knlGS:0000000000000000 -CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 -CR2: 0000000005862000 CR3: 000000022843c000 CR4: 00000000001407e0 -DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 -DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000600 -Stack: - ffff88013deb6dec 000000000000002c 00000000000000c0 ffffffff81a3f6e4 - ffff8801e6431e18 ffffffff8159a9aa ffff8801e6431e90 ffffffff816765f6 - ffffffff810b756b 0000000700000002 ffff8801e6431e40 0000fea9292aa8c0 -Call Trace: - [] skb_push+0x3a/0x40 - [] ip6_push_pending_frames+0x1f6/0x4d0 - [] ? mark_held_locks+0xbb/0x140 - [] udp_v6_push_pending_frames+0x2b9/0x3d0 - [] ? udplite_getfrag+0x20/0x20 - [] udp_lib_setsockopt+0x1aa/0x1f0 - [] ? fget_light+0x387/0x4f0 - [] udpv6_setsockopt+0x34/0x40 - [] sock_common_setsockopt+0x14/0x20 - [] SyS_setsockopt+0x71/0xd0 - [] tracesys+0xdd/0xe2 -Code: 00 00 48 89 44 24 10 8b 87 d8 00 00 00 48 89 44 24 08 48 8b 87 e8 00 00 00 48 c7 c7 c0 04 aa 81 48 89 04 24 31 c0 e8 e1 7e ff ff <0f> 0b 55 48 89 e5 0f 0b 55 48 89 e5 0f 0b 55 48 89 e5 0f 0b 55 -RIP [] skb_panic+0x63/0x65 - RSP - -This patch adds a check if the pending data is of address family AF_INET -and directly calls udp_push_ending_frames from udp_v6_push_pending_frames -if that is the case. - -This bug was found by Dave Jones with trinity. - -(Also move the initialization of fl6 below the AF_INET check, even if -not strictly necessary.) - -Cc: Dave Jones -Cc: YOSHIFUJI Hideaki -Signed-off-by: Hannes Frederic Sowa -Signed-off-by: David S. Miller ---- - include/net/udp.h | 1 + - net/ipv4/udp.c | 3 ++- - net/ipv6/udp.c | 7 ++++++- - 3 files changed, 9 insertions(+), 2 deletions(-) - -diff --git a/include/net/udp.h b/include/net/udp.h -index 065f379..ad99eed 100644 ---- a/include/net/udp.h -+++ b/include/net/udp.h -@@ -181,6 +181,7 @@ extern int udp_get_port(struct sock *sk, unsigned short snum, - extern void udp_err(struct sk_buff *, u32); - extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len); -+extern int udp_push_pending_frames(struct sock *sk); - extern void udp_flush_pending_frames(struct sock *sk); - extern int udp_rcv(struct sk_buff *skb); - extern int udp_ioctl(struct sock *sk, int cmd, unsigned long arg); -diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c -index 0bf5d39..93b731d 100644 ---- a/net/ipv4/udp.c -+++ b/net/ipv4/udp.c -@@ -799,7 +799,7 @@ send: - /* - * Push out all pending data as one UDP datagram. Socket is locked. - */ --static int udp_push_pending_frames(struct sock *sk) -+int udp_push_pending_frames(struct sock *sk) - { - struct udp_sock *up = udp_sk(sk); - struct inet_sock *inet = inet_sk(sk); -@@ -818,6 +818,7 @@ out: - up->pending = 0; - return err; - } -+EXPORT_SYMBOL(udp_push_pending_frames); - - int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len) -diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c -index 42923b1..e7b28f9 100644 ---- a/net/ipv6/udp.c -+++ b/net/ipv6/udp.c -@@ -955,11 +955,16 @@ static int udp_v6_push_pending_frames(struct sock *sk) - struct udphdr *uh; - struct udp_sock *up = udp_sk(sk); - struct inet_sock *inet = inet_sk(sk); -- struct flowi6 *fl6 = &inet->cork.fl.u.ip6; -+ struct flowi6 *fl6; - int err = 0; - int is_udplite = IS_UDPLITE(sk); - __wsum csum = 0; - -+ if (up->pending == AF_INET) -+ return udp_push_pending_frames(sk); -+ -+ fl6 = &inet->cork.fl.u.ip6; -+ - /* Grab the skbuff where UDP header space exists. */ - if ((skb = skb_peek(&sk->sk_write_queue)) == NULL) - goto out; --- -1.7.11.7 diff --git a/ipv6-ip6_append_data_mtu-did-not-care-about-pmtudisc-and_frag_size.patch b/ipv6-ip6_append_data_mtu-did-not-care-about-pmtudisc-and_frag_size.patch deleted file mode 100644 index 97c3b1981..000000000 --- a/ipv6-ip6_append_data_mtu-did-not-care-about-pmtudisc-and_frag_size.patch +++ /dev/null @@ -1,137 +0,0 @@ -From 1fcbda94eb3ababc95eff46548962ceb14de638e Mon Sep 17 00:00:00 2001 -From: Hannes Frederic Sowa -Date: Tue, 2 Jul 2013 08:04:05 +0200 -Subject: [PATCH 12/40] ipv6: ip6_append_data_mtu did not care about pmtudisc - and frag_size - -[ Upstream commit 75a493e60ac4bbe2e977e7129d6d8cbb0dd236be ] - -If the socket had an IPV6_MTU value set, ip6_append_data_mtu lost track -of this when appending the second frame on a corked socket. This results -in the following splat: - -[37598.993962] ------------[ cut here ]------------ -[37598.994008] kernel BUG at net/core/skbuff.c:2064! -[37598.994008] invalid opcode: 0000 [#1] SMP -[37598.994008] Modules linked in: tcp_lp uvcvideo videobuf2_vmalloc videobuf2_memops videobuf2_core videodev media vfat fat usb_storage fuse ebtable_nat xt_CHECKSUM bridge stp llc ipt_MASQUERADE nf_conntrack_netbios_ns nf_conntrack_broadcast ip6table_mangle ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 iptable_nat -+nf_nat_ipv4 nf_nat iptable_mangle nf_conntrack_ipv4 nf_defrag_ipv4 xt_conntrack nf_conntrack ebtable_filter ebtables ip6table_filter ip6_tables be2iscsi iscsi_boot_sysfs bnx2i cnic uio cxgb4i cxgb4 cxgb3i cxgb3 mdio libcxgbi ib_iser rdma_cm ib_addr iw_cm ib_cm ib_sa ib_mad ib_core iscsi_tcp libiscsi_tcp libiscsi -+scsi_transport_iscsi rfcomm bnep iTCO_wdt iTCO_vendor_support snd_hda_codec_conexant arc4 iwldvm mac80211 snd_hda_intel acpi_cpufreq mperf coretemp snd_hda_codec microcode cdc_wdm cdc_acm -[37598.994008] snd_hwdep cdc_ether snd_seq snd_seq_device usbnet mii joydev btusb snd_pcm bluetooth i2c_i801 e1000e lpc_ich mfd_core ptp iwlwifi pps_core snd_page_alloc mei cfg80211 snd_timer thinkpad_acpi snd tpm_tis soundcore rfkill tpm tpm_bios vhost_net tun macvtap macvlan kvm_intel kvm uinput binfmt_misc -+dm_crypt i915 i2c_algo_bit drm_kms_helper drm i2c_core wmi video -[37598.994008] CPU 0 -[37598.994008] Pid: 27320, comm: t2 Not tainted 3.9.6-200.fc18.x86_64 #1 LENOVO 27744PG/27744PG -[37598.994008] RIP: 0010:[] [] skb_copy_and_csum_bits+0x325/0x330 -[37598.994008] RSP: 0018:ffff88003670da18 EFLAGS: 00010202 -[37598.994008] RAX: ffff88018105c018 RBX: 0000000000000004 RCX: 00000000000006c0 -[37598.994008] RDX: ffff88018105a6c0 RSI: ffff88018105a000 RDI: ffff8801e1b0aa00 -[37598.994008] RBP: ffff88003670da78 R08: 0000000000000000 R09: ffff88018105c040 -[37598.994008] R10: ffff8801e1b0aa00 R11: 0000000000000000 R12: 000000000000fff8 -[37598.994008] R13: 00000000000004fc R14: 00000000ffff0504 R15: 0000000000000000 -[37598.994008] FS: 00007f28eea59740(0000) GS:ffff88023bc00000(0000) knlGS:0000000000000000 -[37598.994008] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b -[37598.994008] CR2: 0000003d935789e0 CR3: 00000000365cb000 CR4: 00000000000407f0 -[37598.994008] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 -[37598.994008] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 -[37598.994008] Process t2 (pid: 27320, threadinfo ffff88003670c000, task ffff88022c162ee0) -[37598.994008] Stack: -[37598.994008] ffff88022e098a00 ffff88020f973fc0 0000000000000008 00000000000004c8 -[37598.994008] ffff88020f973fc0 00000000000004c4 ffff88003670da78 ffff8801e1b0a200 -[37598.994008] 0000000000000018 00000000000004c8 ffff88020f973fc0 00000000000004c4 -[37598.994008] Call Trace: -[37598.994008] [] ip6_append_data+0xccf/0xfe0 -[37598.994008] [] ? ip_copy_metadata+0x1a0/0x1a0 -[37598.994008] [] ? _raw_spin_lock_bh+0x16/0x40 -[37598.994008] [] udpv6_sendmsg+0x1ed/0xc10 -[37598.994008] [] ? sock_has_perm+0x75/0x90 -[37598.994008] [] inet_sendmsg+0x63/0xb0 -[37598.994008] [] ? selinux_socket_sendmsg+0x23/0x30 -[37598.994008] [] sock_sendmsg+0xb0/0xe0 -[37598.994008] [] ? __switch_to+0x181/0x4a0 -[37598.994008] [] sys_sendto+0x12d/0x180 -[37598.994008] [] ? __audit_syscall_entry+0x94/0xf0 -[37598.994008] [] ? syscall_trace_enter+0x231/0x240 -[37598.994008] [] tracesys+0xdd/0xe2 -[37598.994008] Code: fe 07 00 00 48 c7 c7 04 28 a6 81 89 45 a0 4c 89 4d b8 44 89 5d a8 e8 1b ac b1 ff 44 8b 5d a8 4c 8b 4d b8 8b 45 a0 e9 cf fe ff ff <0f> 0b 66 0f 1f 84 00 00 00 00 00 66 66 66 66 90 55 48 89 e5 48 -[37598.994008] RIP [] skb_copy_and_csum_bits+0x325/0x330 -[37598.994008] RSP -[37599.007323] ---[ end trace d69f6a17f8ac8eee ]--- - -While there, also check if path mtu discovery is activated for this -socket. The logic was adapted from ip6_append_data when first writing -on the corked socket. - -This bug was introduced with commit -0c1833797a5a6ec23ea9261d979aa18078720b74 ("ipv6: fix incorrect ipsec -fragment"). - -v2: -a) Replace IPV6_PMTU_DISC_DO with IPV6_PMTUDISC_PROBE. -b) Don't pass ipv6_pinfo to ip6_append_data_mtu (suggestion by Gao - feng, thanks!). -c) Change mtu to unsigned int, else we get a warning about - non-matching types because of the min()-macro type-check. - -Acked-by: Gao feng -Cc: YOSHIFUJI Hideaki -Signed-off-by: Hannes Frederic Sowa -Signed-off-by: David S. Miller ---- - net/ipv6/ip6_output.c | 16 ++++++++++------ - 1 file changed, 10 insertions(+), 6 deletions(-) - -diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c -index d5d20cd..6e3ddf8 100644 ---- a/net/ipv6/ip6_output.c -+++ b/net/ipv6/ip6_output.c -@@ -1098,11 +1098,12 @@ static inline struct ipv6_rt_hdr *ip6_rthdr_dup(struct ipv6_rt_hdr *src, - return src ? kmemdup(src, (src->hdrlen + 1) * 8, gfp) : NULL; - } - --static void ip6_append_data_mtu(int *mtu, -+static void ip6_append_data_mtu(unsigned int *mtu, - int *maxfraglen, - unsigned int fragheaderlen, - struct sk_buff *skb, -- struct rt6_info *rt) -+ struct rt6_info *rt, -+ bool pmtuprobe) - { - if (!(rt->dst.flags & DST_XFRM_TUNNEL)) { - if (skb == NULL) { -@@ -1114,7 +1115,9 @@ static void ip6_append_data_mtu(int *mtu, - * this fragment is not first, the headers - * space is regarded as data space. - */ -- *mtu = dst_mtu(rt->dst.path); -+ *mtu = min(*mtu, pmtuprobe ? -+ rt->dst.dev->mtu : -+ dst_mtu(rt->dst.path)); - } - *maxfraglen = ((*mtu - fragheaderlen) & ~7) - + fragheaderlen - sizeof(struct frag_hdr); -@@ -1131,11 +1134,10 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, - struct ipv6_pinfo *np = inet6_sk(sk); - struct inet_cork *cork; - struct sk_buff *skb, *skb_prev = NULL; -- unsigned int maxfraglen, fragheaderlen; -+ unsigned int maxfraglen, fragheaderlen, mtu; - int exthdrlen; - int dst_exthdrlen; - int hh_len; -- int mtu; - int copy; - int err; - int offset = 0; -@@ -1292,7 +1294,9 @@ alloc_new_skb: - /* update mtu and maxfraglen if necessary */ - if (skb == NULL || skb_prev == NULL) - ip6_append_data_mtu(&mtu, &maxfraglen, -- fragheaderlen, skb, rt); -+ fragheaderlen, skb, rt, -+ np->pmtudisc == -+ IPV6_PMTUDISC_PROBE); - - skb_prev = skb; - --- -1.7.11.7 diff --git a/ipv6-ip6_sk_dst_check-must-not-assume-ipv6-dst.patch b/ipv6-ip6_sk_dst_check-must-not-assume-ipv6-dst.patch deleted file mode 100644 index 8f6c41d28..000000000 --- a/ipv6-ip6_sk_dst_check-must-not-assume-ipv6-dst.patch +++ /dev/null @@ -1,52 +0,0 @@ -From a963a37d384d71ad43b3e9e79d68d42fbe0901f3 Mon Sep 17 00:00:00 2001 -From: Eric Dumazet -Date: Wed, 26 Jun 2013 04:15:07 -0700 -Subject: [PATCH] ipv6: ip6_sk_dst_check() must not assume ipv6 dst - -It's possible to use AF_INET6 sockets and to connect to an IPv4 -destination. After this, socket dst cache is a pointer to a rtable, -not rt6_info. - -ip6_sk_dst_check() should check the socket dst cache is IPv6, or else -various corruptions/crashes can happen. - -Dave Jones can reproduce immediate crash with -trinity -q -l off -n -c sendmsg -c connect - -With help from Hannes Frederic Sowa - -Reported-by: Dave Jones -Reported-by: Hannes Frederic Sowa -Signed-off-by: Eric Dumazet -Acked-by: Hannes Frederic Sowa -Signed-off-by: David S. Miller ---- - net/ipv6/ip6_output.c | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - -diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c -index 95703ba..d5d20cd 100644 ---- a/net/ipv6/ip6_output.c -+++ b/net/ipv6/ip6_output.c -@@ -821,11 +821,17 @@ static struct dst_entry *ip6_sk_dst_check(struct sock *sk, - const struct flowi6 *fl6) - { - struct ipv6_pinfo *np = inet6_sk(sk); -- struct rt6_info *rt = (struct rt6_info *)dst; -+ struct rt6_info *rt; - - if (!dst) - goto out; - -+ if (dst->ops->family != AF_INET6) { -+ dst_release(dst); -+ return NULL; -+ } -+ -+ rt = (struct rt6_info *)dst; - /* Yes, checking route validity in not connected - * case is not very simple. Take into account, - * that we do not support routing by source, TOS, --- -1.8.2.1 - diff --git a/iwlwifi-add-new-pci-id-for-6x35-series.patch b/iwlwifi-add-new-pci-id-for-6x35-series.patch deleted file mode 100644 index ba616400f..000000000 --- a/iwlwifi-add-new-pci-id-for-6x35-series.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 20ecf9fd3bebc4147e2996c08a75d6f0229b90df Mon Sep 17 00:00:00 2001 -From: Shuduo Sang -Date: Sat, 30 Mar 2013 06:26:37 +0000 -Subject: iwlwifi: add new pci id for 6x35 series - -some new thinkpad laptops use intel chip with new pci id need be added -lspci -vnn output: - Network controller [0280]: Intel Corporation Centrino Advanced-N 6235 - [8086:088f] (rev 24) - Subsystem: Intel Corporation Device [8086:5260] - -Signed-off-by: Shuduo Sang -Reviewed-by: Emmanuel Grumbach -Signed-off-by: Johannes Berg ---- -diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c -index 46ca91f..0016bb2 100644 ---- a/drivers/net/wireless/iwlwifi/pcie/drv.c -+++ b/drivers/net/wireless/iwlwifi/pcie/drv.c -@@ -241,6 +241,7 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { - {IWL_PCI_DEVICE(0x088F, 0x4260, iwl6035_2agn_cfg)}, - {IWL_PCI_DEVICE(0x088E, 0x4460, iwl6035_2agn_cfg)}, - {IWL_PCI_DEVICE(0x088E, 0x4860, iwl6035_2agn_cfg)}, -+ {IWL_PCI_DEVICE(0x088F, 0x5260, iwl6035_2agn_cfg)}, - - /* 105 Series */ - {IWL_PCI_DEVICE(0x0894, 0x0022, iwl105_bgn_cfg)}, --- -cgit v0.9.2 diff --git a/iwlwifi-pcie-fix-race-in-queue-unmapping.patch b/iwlwifi-pcie-fix-race-in-queue-unmapping.patch deleted file mode 100644 index ad9194af3..000000000 --- a/iwlwifi-pcie-fix-race-in-queue-unmapping.patch +++ /dev/null @@ -1,56 +0,0 @@ -From: Emmanuel Grumbach - -When a queue is disabled, it frees all its entries. Later, -the op_mode might still get notifications from the firmware -that triggers to free entries in the tx queue. The transport -should be prepared for these races and know to ignore -reclaim calls on queues that have been disabled and whose -entries have been freed. - -Cc: stable@vger.kernel.org -Signed-off-by: Emmanuel Grumbach -Signed-off-by: Johannes Berg ---- - drivers/net/wireless/iwlwifi/pcie/tx.c | 10 ++++++++++ - 1 file changed, 10 insertions(+) - -diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c -index cb5c679..faaf77c 100644 ---- a/drivers/net/wireless/iwlwifi/pcie/tx.c -+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c -@@ -578,9 +578,12 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) - - spin_lock_bh(&txq->lock); - while (q->write_ptr != q->read_ptr) { -+ IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n", -+ txq_id, q->read_ptr); - iwl_pcie_txq_free_tfd(trans, txq); - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); - } -+ txq->active = false; - spin_unlock_bh(&txq->lock); - } - -@@ -929,6 +932,12 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, - - spin_lock_bh(&txq->lock); - -+ if (!txq->active) { -+ IWL_DEBUG_TX_QUEUES(trans, "Q %d inactive - ignoring idx %d\n", -+ txq_id, ssn); -+ goto out; -+ } -+ - if (txq->q.read_ptr == tfd_num) - goto out; - -@@ -1105,6 +1114,7 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, - (fifo << SCD_QUEUE_STTS_REG_POS_TXF) | - (1 << SCD_QUEUE_STTS_REG_POS_WSL) | - SCD_QUEUE_STTS_REG_MSK); -+ trans_pcie->txq[txq_id].active = true; - IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d on FIFO %d WrPtr: %d\n", - txq_id, fifo, ssn & 0xff); - } --- -1.7.11.7 diff --git a/iwlwifi-pcie-wake-the-queue-if-stopped-when-being-unmapped.patch b/iwlwifi-pcie-wake-the-queue-if-stopped-when-being-unmapped.patch deleted file mode 100644 index 661fc5035..000000000 --- a/iwlwifi-pcie-wake-the-queue-if-stopped-when-being-unmapped.patch +++ /dev/null @@ -1,35 +0,0 @@ -From: Emmanuel Grumbach - -When the queue is unmapped while it was so loaded that -mac80211's was stopped, we need to wake the queue after -having freed all the packets in the queue. -Not doing so can result in weird stuff like: - -* run lots of traffic (mac80211's queue gets stopped) -* RFKILL -* de-assert RFKILL -* no traffic - -Cc: stable@vger.kernel.org -Signed-off-by: Emmanuel Grumbach -Signed-off-by: Johannes Berg ---- - drivers/net/wireless/iwlwifi/pcie/tx.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c -index faaf77c..4e7b8d4 100644 ---- a/drivers/net/wireless/iwlwifi/pcie/tx.c -+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c -@@ -585,6 +585,9 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) - } - txq->active = false; - spin_unlock_bh(&txq->lock); -+ -+ /* just in case - this queue may have been stopped */ -+ iwl_wake_queue(trans, txq); - } - - /* --- -1.7.11.7 diff --git a/kernel.spec b/kernel.spec index 517280272..9098ef4e9 100644 --- a/kernel.spec +++ b/kernel.spec @@ -62,19 +62,19 @@ Summary: The Linux kernel # For non-released -rc kernels, this will be appended after the rcX and # gitX tags, so a 3 here would become part of release "0.rcX.gitX.3" # -%global baserelease 201 +%global baserelease 100 %global fedora_build %{baserelease} # base_sublevel is the kernel version we're starting with and patching # on top of -- for example, 3.1-rc7-git1 starts with a 3.0 base, # which yields a base_sublevel of 0. -%define base_sublevel 9 +%define base_sublevel 10 ## If this is a released kernel ## %if 0%{?released_kernel} # Do we have a -stable update to apply? -%define stable_update 11 +%define stable_update 4 # Is it a -stable RC? %define stable_rc 0 # Set rpm version accordingly @@ -649,9 +649,6 @@ Patch100: taint-vbox.patch Patch110: vmbugon-warnon.patch -Patch200: debug-bad-pte-dmi.patch -Patch201: debug-bad-pte-modules.patch - Patch390: defaults-acpi-video.patch Patch391: acpi-video-dos.patch Patch394: acpi-debug-infinite-loop.patch @@ -670,7 +667,7 @@ Patch530: silence-fbcon-logo.patch Patch800: crash-driver.patch # secure boot -Patch1000: secure-boot-20130506.patch +Patch1000: devel-pekey-secure-boot-20130502.patch # virt + ksm patches @@ -718,11 +715,9 @@ Patch20001: 0002-x86-EFI-Calculate-the-EFI-framebuffer-size-instead-o.patch Patch21000: arm-export-read_current_timer.patch # ARM omap -Patch21003: arm-omap-ehci-fix.patch # ARM tegra Patch21005: arm-tegra-usb-no-reset-linux33.patch -Patch21006: arm-tegra-fixclk.patch #rhbz 754518 Patch21235: scsi-sd_revalidate_disk-prevent-NULL-ptr-deref.patch @@ -738,15 +733,6 @@ Patch22226: vt-Drop-K_OFF-for-VC_MUTE.patch #rhbz 892811 Patch22247: ath9k_rx_dma_stop_check.patch -#rhbz 916544 -Patch22263: 0001-drivers-crypto-nx-fix-init-race-alignmasks-and-GCM-b.patch - -#rhbz 859282 -Patch24113: VMX-x86-handle-host-TSC-calibration-failure.patch - -#rhbz 921500 -Patch25001: i7300_edac_single_mode_fixup.patch - #rhbz 927469 Patch25007: fix-child-thread-introspection.patch @@ -765,12 +751,6 @@ Patch25033: fanotify-info-leak-in-copy_event_to_user.patch #rhbz 969644 Patch25046: KVM-x86-handle-idiv-overflow-at-kvm_write_tsc.patch -#rhbz 975995 -Patch25047: drivers-hwmon-nct6775.patch - -Patch25050: iwlwifi-pcie-fix-race-in-queue-unmapping.patch -Patch25051: iwlwifi-pcie-wake-the-queue-if-stopped-when-being-unmapped.patch - #rhbz 903741 Patch25052: HID-input-return-ENODATA-if-reading-battery-attrs-fails.patch @@ -788,38 +768,21 @@ Patch25055: ath3k-dont-use-stack-memory-for-DMA.patch Patch25056: iwl3945-better-skb-management-in-rx-path.patch Patch25057: iwl4965-better-skb-management-in-rx-path.patch -#CVE-2013-2234 rhbz 980995 981007 -Patch25058: af_key-fix-info-leaks-in-notify-messages.patch - -#CVE-2013-2232 rhbz 981552 981564 -Patch25060: ipv6-ip6_sk_dst_check-must-not-assume-ipv6-dst.patch - -#rhbz 976789 980643 -Patch25062: vhost-net-fix-use-after-free-in-vhost_net_flush.patch - #rhbz 959721 Patch25063: HID-kye-Add-report-fixup-for-Genius-Gila-Gaming-mouse.patch #rhbz 885407 Patch25064: iwlwifi-dvm-dont-send-BT_CONFIG-on-devices-wo-Bluetooth.patch -#rhbz 986538 -Patch25065: iwlwifi-add-new-pci-id-for-6x35-series.patch - -#CVE-2013-4163 rhbz 987633 987639 -Patch25067: ipv6-ip6_append_data_mtu-did-not-care-about-pmtudisc-and_frag_size.patch - -#CVE-2013-4162 rhbz 987627 987656 -Patch25068: ipv6-call-udp_push_pending_frames-when-uncorking-a-socket-with-AF_INET-pending-data.patch - -Patch26000: cve-2013-4125.patch - #rhbz 979581 Patch25069: iwlwifi-dvm-fix-calling-ieee80211_chswitch_done-with-NULL.patch #rhbz 969473 Patch25070: Input-elantech-fix-for-newer-hardware-versions-v7.patch +#rhbz 989093 +Patch25071: drm-i915-correctly-restore-fences-with-objects-attac.patch + #rhbz 977053 Patch25073: iwl4965-reset-firmware-after-rfkill-off.patch @@ -1373,10 +1336,6 @@ ApplyPatch taint-vbox.patch ApplyPatch vmbugon-warnon.patch -ApplyPatch debug-bad-pte-dmi.patch -ApplyPatch debug-bad-pte-modules.patch - - # Architecture patches # x86(-64) @@ -1384,9 +1343,7 @@ ApplyPatch debug-bad-pte-modules.patch # ARM # ApplyPatch arm-export-read_current_timer.patch -ApplyPatch arm-omap-ehci-fix.patch ApplyPatch arm-tegra-usb-no-reset-linux33.patch -ApplyPatch arm-tegra-fixclk.patch # # bugfixes to drivers and filesystems @@ -1451,7 +1408,7 @@ ApplyPatch silence-fbcon-logo.patch ApplyPatch crash-driver.patch # secure boot -ApplyPatch secure-boot-20130506.patch +ApplyPatch devel-pekey-secure-boot-20130502.patch # Assorted Virt Fixes @@ -1504,15 +1461,6 @@ ApplyPatch vt-Drop-K_OFF-for-VC_MUTE.patch #rhbz 892811 ApplyPatch ath9k_rx_dma_stop_check.patch -#rhbz 916544 -ApplyPatch 0001-drivers-crypto-nx-fix-init-race-alignmasks-and-GCM-b.patch - -#rhbz 921500 -ApplyPatch i7300_edac_single_mode_fixup.patch - -#rhbz 859282 -ApplyPatch VMX-x86-handle-host-TSC-calibration-failure.patch - #rhbz 927469 ApplyPatch fix-child-thread-introspection.patch @@ -1531,12 +1479,6 @@ ApplyPatch fanotify-info-leak-in-copy_event_to_user.patch #rhbz 969644 ApplyPatch KVM-x86-handle-idiv-overflow-at-kvm_write_tsc.patch -#rhbz 975995 -ApplyPatch drivers-hwmon-nct6775.patch - -ApplyPatch iwlwifi-pcie-fix-race-in-queue-unmapping.patch -ApplyPatch iwlwifi-pcie-wake-the-queue-if-stopped-when-being-unmapped.patch - #rhbz 903741 ApplyPatch HID-input-return-ENODATA-if-reading-battery-attrs-fails.patch @@ -1554,38 +1496,21 @@ ApplyPatch ath3k-dont-use-stack-memory-for-DMA.patch ApplyPatch iwl3945-better-skb-management-in-rx-path.patch ApplyPatch iwl4965-better-skb-management-in-rx-path.patch -#CVE-2013-2234 rhbz 980995 981007 -ApplyPatch af_key-fix-info-leaks-in-notify-messages.patch - -#CVE-2013-2232 rhbz 981552 981564 -ApplyPatch ipv6-ip6_sk_dst_check-must-not-assume-ipv6-dst.patch - -#rhbz 976789 980643 -ApplyPatch vhost-net-fix-use-after-free-in-vhost_net_flush.patch - #rhbz 959721 ApplyPatch HID-kye-Add-report-fixup-for-Genius-Gila-Gaming-mouse.patch #rhbz 885407 ApplyPatch iwlwifi-dvm-dont-send-BT_CONFIG-on-devices-wo-Bluetooth.patch -ApplyPatch cve-2013-4125.patch - -#rhbz 986538 -ApplyPatch iwlwifi-add-new-pci-id-for-6x35-series.patch - -#CVE-2013-4163 rhbz 987633 987639 -ApplyPatch ipv6-ip6_append_data_mtu-did-not-care-about-pmtudisc-and_frag_size.patch - -#CVE-2013-4162 rhbz 987627 987656 -ApplyPatch ipv6-call-udp_push_pending_frames-when-uncorking-a-socket-with-AF_INET-pending-data.patch - #rhbz 979581 ApplyPatch iwlwifi-dvm-fix-calling-ieee80211_chswitch_done-with-NULL.patch #rhbz 969473 ApplyPatch Input-elantech-fix-for-newer-hardware-versions-v7.patch +#rhbz 989093 +ApplyPatch drm-i915-correctly-restore-fences-with-objects-attac.patch + #rhbz 977053 ApplyPatch iwl4965-reset-firmware-after-rfkill-off.patch @@ -2439,6 +2364,26 @@ fi # ||----w | # || || %changelog +* Thu Aug 01 2013 Justin M. Forbes +- Rebase to 3.10.4 + dropped: + debug-bad-pte-dmi.patch + debug-bad-pte-modules.patch + VMX-x86-handle-host-TSC-calibration-failure.patch + ipv6-ip6_sk_dst_check-must-not-assume-ipv6-dst.patch + af_key-fix-info-leaks-in-notify-messages.patch + arm-tegra-fixclk.patch + vhost-net-fix-use-after-free-in-vhost_net_flush.patch + 0001-drivers-crypto-nx-fix-init-race-alignmasks-and-GCM-b.patch + i7300_edac_single_mode_fixup.patch + drivers-hwmon-nct6775.patch + iwlwifi-pcie-fix-race-in-queue-unmapping.patch + iwlwifi-pcie-wake-the-queue-if-stopped-when-being-unmapped.patch + cve-2013-4125.patch + iwlwifi-add-new-pci-id-for-6x35-series.patch + ipv6-ip6_append_data_mtu-did-not-care-about-pmtudisc-and_frag_size.patch + ipv6-call-udp_push_pending_frames-when-uncorking-a-socket-with-AF_INET-pending-data.patch + * Thu Aug 01 2013 Josh Boyer - Fix mac80211 connection issues (rhbz 981445) - Fix firmware issues with iwl4965 and rfkill (rhbz 977053) diff --git a/secure-boot-20130506.patch b/secure-boot-20130506.patch deleted file mode 100644 index f2267f23a..000000000 --- a/secure-boot-20130506.patch +++ /dev/null @@ -1,1474 +0,0 @@ -From 5b93084a9e220fb63d549c0acba389dc39c12ea5 Mon Sep 17 00:00:00 2001 -From: Matthew Garrett -Date: Thu, 20 Sep 2012 10:40:56 -0400 -Subject: [PATCH 01/19] Secure boot: Add new capability - -Secure boot adds certain policy requirements, including that root must not -be able to do anything that could cause the kernel to execute arbitrary code. -The simplest way to handle this would seem to be to add a new capability -and gate various functionality on that. We'll then strip it from the initial -capability set if required. - -Signed-off-by: Matthew Garrett ---- - include/uapi/linux/capability.h | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/include/uapi/linux/capability.h b/include/uapi/linux/capability.h -index ba478fa..7109e65 100644 ---- a/include/uapi/linux/capability.h -+++ b/include/uapi/linux/capability.h -@@ -343,7 +343,11 @@ struct vfs_cap_data { - - #define CAP_BLOCK_SUSPEND 36 - --#define CAP_LAST_CAP CAP_BLOCK_SUSPEND -+/* Allow things that trivially permit root to modify the running kernel */ -+ -+#define CAP_COMPROMISE_KERNEL 37 -+ -+#define CAP_LAST_CAP CAP_COMPROMISE_KERNEL - - #define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP) - --- -1.8.1.4 - - -From 01cd96f927fd11fd43ba4631bd7a314e09ca9939 Mon Sep 17 00:00:00 2001 -From: Josh Boyer -Date: Thu, 20 Sep 2012 10:41:05 -0400 -Subject: [PATCH 02/19] SELinux: define mapping for new Secure Boot capability - -Add the name of the new Secure Boot capability. This allows SELinux -policies to properly map CAP_COMPROMISE_KERNEL to the appropriate -capability class. - -Signed-off-by: Josh Boyer ---- - security/selinux/include/classmap.h | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h -index 14d04e6..ed99a2d 100644 ---- a/security/selinux/include/classmap.h -+++ b/security/selinux/include/classmap.h -@@ -146,8 +146,8 @@ struct security_class_mapping secclass_map[] = { - { "memprotect", { "mmap_zero", NULL } }, - { "peer", { "recv", NULL } }, - { "capability2", -- { "mac_override", "mac_admin", "syslog", "wake_alarm", "block_suspend", -- NULL } }, -+ { "mac_override", "mac_admin", "syslog", "wake_alarm", -+ "block_suspend", "compromise_kernel", NULL } }, - { "kernel_service", { "use_as_override", "create_files_as", NULL } }, - { "tun_socket", - { COMMON_SOCK_PERMS, "attach_queue", NULL } }, --- -1.8.1.4 - - -From aabe98772f0b63652df87a2dd566ad2e66fbe14f Mon Sep 17 00:00:00 2001 -From: Josh Boyer -Date: Thu, 20 Sep 2012 10:41:02 -0400 -Subject: [PATCH 03/19] Secure boot: Add a dummy kernel parameter that will - switch on Secure Boot mode - -This forcibly drops CAP_COMPROMISE_KERNEL from both cap_permitted and cap_bset -in the init_cred struct, which everything else inherits from. This works on -any machine and can be used to develop even if the box doesn't have UEFI. - -Signed-off-by: Josh Boyer ---- - Documentation/kernel-parameters.txt | 7 +++++++ - kernel/cred.c | 17 +++++++++++++++++ - 2 files changed, 24 insertions(+) - -diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt -index 8ccbf27..5bd14a4 100644 ---- a/Documentation/kernel-parameters.txt -+++ b/Documentation/kernel-parameters.txt -@@ -2706,6 +2706,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted. - Note: increases power consumption, thus should only be - enabled if running jitter sensitive (HPC/RT) workloads. - -+ secureboot_enable= -+ [KNL] Enables an emulated UEFI Secure Boot mode. This -+ locks down various aspects of the kernel guarded by the -+ CAP_COMPROMISE_KERNEL capability. This includes things -+ like /dev/mem, IO port access, and other areas. It can -+ be used on non-UEFI machines for testing purposes. -+ - security= [SECURITY] Choose a security module to enable at boot. - If this boot parameter is not specified, only the first - security module asking for security registration will be -diff --git a/kernel/cred.c b/kernel/cred.c -index e0573a4..c3f4e3e 100644 ---- a/kernel/cred.c -+++ b/kernel/cred.c -@@ -565,6 +565,23 @@ void __init cred_init(void) - 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); - } - -+void __init secureboot_enable() -+{ -+ pr_info("Secure boot enabled\n"); -+ cap_lower((&init_cred)->cap_bset, CAP_COMPROMISE_KERNEL); -+ cap_lower((&init_cred)->cap_permitted, CAP_COMPROMISE_KERNEL); -+} -+ -+/* Dummy Secure Boot enable option to fake out UEFI SB=1 */ -+static int __init secureboot_enable_opt(char *str) -+{ -+ int sb_enable = !!simple_strtol(str, NULL, 0); -+ if (sb_enable) -+ secureboot_enable(); -+ return 1; -+} -+__setup("secureboot_enable=", secureboot_enable_opt); -+ - /** - * prepare_kernel_cred - Prepare a set of credentials for a kernel service - * @daemon: A userspace daemon to be used as a reference --- -1.8.1.4 - - -From 76de11cca755aca928ed2f6b6c99ffc3328cde13 Mon Sep 17 00:00:00 2001 -From: Matthew Garrett -Date: Thu, 20 Sep 2012 10:41:03 -0400 -Subject: [PATCH 04/19] efi: Enable secure boot lockdown automatically when - enabled in firmware - -The firmware has a set of flags that indicate whether secure boot is enabled -and enforcing. Use them to indicate whether the kernel should lock itself -down. We also indicate the machine is in secure boot mode by adding the -EFI_SECURE_BOOT bit for use with efi_enabled. - -Signed-off-by: Matthew Garrett -Signed-off-by: Josh Boyer ---- - Documentation/x86/zero-page.txt | 2 ++ - arch/x86/boot/compressed/eboot.c | 32 ++++++++++++++++++++++++++++++++ - arch/x86/include/asm/bootparam_utils.h | 8 ++++++-- - arch/x86/include/uapi/asm/bootparam.h | 3 ++- - arch/x86/kernel/setup.c | 7 +++++++ - include/linux/cred.h | 2 ++ - include/linux/efi.h | 1 + - 7 files changed, 52 insertions(+), 3 deletions(-) - -diff --git a/Documentation/x86/zero-page.txt b/Documentation/x86/zero-page.txt -index 199f453..ff651d3 100644 ---- a/Documentation/x86/zero-page.txt -+++ b/Documentation/x86/zero-page.txt -@@ -30,6 +30,8 @@ Offset Proto Name Meaning - 1E9/001 ALL eddbuf_entries Number of entries in eddbuf (below) - 1EA/001 ALL edd_mbr_sig_buf_entries Number of entries in edd_mbr_sig_buffer - (below) -+1EB/001 ALL kbd_status Numlock is enabled -+1EC/001 ALL secure_boot Kernel should enable secure boot lockdowns - 1EF/001 ALL sentinel Used to detect broken bootloaders - 290/040 ALL edd_mbr_sig_buffer EDD MBR signatures - 2D0/A00 ALL e820_map E820 memory map table -diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c -index 35ee62f..0998ec7 100644 ---- a/arch/x86/boot/compressed/eboot.c -+++ b/arch/x86/boot/compressed/eboot.c -@@ -906,6 +906,36 @@ fail: - return status; - } - -+static int get_secure_boot(efi_system_table_t *_table) -+{ -+ u8 sb, setup; -+ unsigned long datasize = sizeof(sb); -+ efi_guid_t var_guid = EFI_GLOBAL_VARIABLE_GUID; -+ efi_status_t status; -+ -+ status = efi_call_phys5(sys_table->runtime->get_variable, -+ L"SecureBoot", &var_guid, NULL, &datasize, &sb); -+ -+ if (status != EFI_SUCCESS) -+ return 0; -+ -+ if (sb == 0) -+ return 0; -+ -+ -+ status = efi_call_phys5(sys_table->runtime->get_variable, -+ L"SetupMode", &var_guid, NULL, &datasize, -+ &setup); -+ -+ if (status != EFI_SUCCESS) -+ return 0; -+ -+ if (setup == 1) -+ return 0; -+ -+ return 1; -+} -+ - /* - * Because the x86 boot code expects to be passed a boot_params we - * need to create one ourselves (usually the bootloader would create -@@ -1200,6 +1230,8 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table, - if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) - goto fail; - -+ boot_params->secure_boot = get_secure_boot(sys_table); -+ - setup_graphics(boot_params); - - setup_efi_vars(boot_params); -diff --git a/arch/x86/include/asm/bootparam_utils.h b/arch/x86/include/asm/bootparam_utils.h -index 653668d..7856ca7 100644 ---- a/arch/x86/include/asm/bootparam_utils.h -+++ b/arch/x86/include/asm/bootparam_utils.h -@@ -38,9 +38,13 @@ static void sanitize_boot_params(struct boot_params *boot_params) - memset(&boot_params->olpc_ofw_header, 0, - (char *)&boot_params->efi_info - - (char *)&boot_params->olpc_ofw_header); -- memset(&boot_params->kbd_status, 0, -+ memset(&boot_params->kbd_status, 0, sizeof(boot_params->kbd_status)); -+ /* don't clear boot_params->secure_boot. we set that ourselves -+ * earlier. -+ */ -+ memset(&boot_params->_pad5[0], 0, - (char *)&boot_params->hdr - -- (char *)&boot_params->kbd_status); -+ (char *)&boot_params->_pad5[0]); - memset(&boot_params->_pad7[0], 0, - (char *)&boot_params->edd_mbr_sig_buffer[0] - - (char *)&boot_params->_pad7[0]); -diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h -index 0874424..56b7d39 100644 ---- a/arch/x86/include/uapi/asm/bootparam.h -+++ b/arch/x86/include/uapi/asm/bootparam.h -@@ -132,7 +132,8 @@ struct boot_params { - __u8 eddbuf_entries; /* 0x1e9 */ - __u8 edd_mbr_sig_buf_entries; /* 0x1ea */ - __u8 kbd_status; /* 0x1eb */ -- __u8 _pad5[3]; /* 0x1ec */ -+ __u8 secure_boot; /* 0x1ec */ -+ __u8 _pad5[2]; /* 0x1ed */ - /* - * The sentinel is set to a nonzero value (0xff) in header.S. - * -diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c -index fae9134..b7465a5 100644 ---- a/arch/x86/kernel/setup.c -+++ b/arch/x86/kernel/setup.c -@@ -1133,6 +1133,13 @@ void __init setup_arch(char **cmdline_p) - - io_delay_init(); - -+ if (boot_params.secure_boot) { -+#ifdef CONFIG_EFI -+ set_bit(EFI_SECURE_BOOT, &x86_efi_facility); -+#endif -+ secureboot_enable(); -+ } -+ - /* - * Parse the ACPI tables for possible boot-time SMP configuration. - */ -diff --git a/include/linux/cred.h b/include/linux/cred.h -index 04421e8..9e69542 100644 ---- a/include/linux/cred.h -+++ b/include/linux/cred.h -@@ -156,6 +156,8 @@ extern int set_security_override_from_ctx(struct cred *, const char *); - extern int set_create_files_as(struct cred *, struct inode *); - extern void __init cred_init(void); - -+extern void secureboot_enable(void); -+ - /* - * check for validity of credentials - */ -diff --git a/include/linux/efi.h b/include/linux/efi.h -index 3d7df3d..178b32c 100644 ---- a/include/linux/efi.h -+++ b/include/linux/efi.h -@@ -634,6 +634,7 @@ extern int __init efi_setup_pcdp_console(char *); - #define EFI_RUNTIME_SERVICES 3 /* Can we use runtime services? */ - #define EFI_MEMMAP 4 /* Can we use EFI memory map? */ - #define EFI_64BIT 5 /* Is the firmware 64-bit? */ -+#define EFI_SECURE_BOOT 6 /* Are we in Secure Boot mode? */ - - #ifdef CONFIG_EFI - # ifdef CONFIG_X86 --- -1.8.1.4 - - -From cd3b94aed727f9f77901e319058f2bc7257e64c8 Mon Sep 17 00:00:00 2001 -From: Dave Howells -Date: Tue, 23 Oct 2012 09:30:54 -0400 -Subject: [PATCH 05/19] Add EFI signature data types - -Add the data types that are used for containing hashes, keys and certificates -for cryptographic verification. - -Signed-off-by: David Howells ---- - include/linux/efi.h | 20 ++++++++++++++++++++ - 1 file changed, 20 insertions(+) - -diff --git a/include/linux/efi.h b/include/linux/efi.h -index 178b32c..77373a7 100644 ---- a/include/linux/efi.h -+++ b/include/linux/efi.h -@@ -389,6 +389,12 @@ typedef efi_status_t efi_query_variable_store_t(u32 attributes, unsigned long si - #define EFI_FILE_SYSTEM_GUID \ - EFI_GUID( 0x964e5b22, 0x6459, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b ) - -+#define EFI_CERT_SHA256_GUID \ -+ EFI_GUID( 0xc1c41626, 0x504c, 0x4092, 0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28 ) -+ -+#define EFI_CERT_X509_GUID \ -+ EFI_GUID( 0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72 ) -+ - typedef struct { - efi_guid_t guid; - u64 table; -@@ -524,6 +530,20 @@ typedef struct { - - #define EFI_INVALID_TABLE_ADDR (~0UL) - -+typedef struct { -+ efi_guid_t signature_owner; -+ u8 signature_data[]; -+} efi_signature_data_t; -+ -+typedef struct { -+ efi_guid_t signature_type; -+ u32 signature_list_size; -+ u32 signature_header_size; -+ u32 signature_size; -+ u8 signature_header[]; -+ /* efi_signature_data_t signatures[][] */ -+} efi_signature_list_t; -+ - /* - * All runtime access to EFI goes through this structure: - */ --- -1.8.1.4 - - -From 62e8fff1a3d879c9377f3e8b9bb3aac2bc115b5f Mon Sep 17 00:00:00 2001 -From: Dave Howells -Date: Tue, 23 Oct 2012 09:36:28 -0400 -Subject: [PATCH 06/19] Add an EFI signature blob parser and key loader. - -X.509 certificates are loaded into the specified keyring as asymmetric type -keys. - -Signed-off-by: David Howells ---- - crypto/asymmetric_keys/Kconfig | 8 +++ - crypto/asymmetric_keys/Makefile | 1 + - crypto/asymmetric_keys/efi_parser.c | 108 ++++++++++++++++++++++++++++++++++++ - include/linux/efi.h | 4 ++ - 4 files changed, 121 insertions(+) - create mode 100644 crypto/asymmetric_keys/efi_parser.c - -diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig -index 6d2c2ea..ace9c30 100644 ---- a/crypto/asymmetric_keys/Kconfig -+++ b/crypto/asymmetric_keys/Kconfig -@@ -35,4 +35,12 @@ config X509_CERTIFICATE_PARSER - data and provides the ability to instantiate a crypto key from a - public key packet found inside the certificate. - -+config EFI_SIGNATURE_LIST_PARSER -+ bool "EFI signature list parser" -+ depends on EFI -+ select X509_CERTIFICATE_PARSER -+ help -+ This option provides support for parsing EFI signature lists for -+ X.509 certificates and turning them into keys. -+ - endif # ASYMMETRIC_KEY_TYPE -diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile -index 0727204..cd8388e 100644 ---- a/crypto/asymmetric_keys/Makefile -+++ b/crypto/asymmetric_keys/Makefile -@@ -8,6 +8,7 @@ asymmetric_keys-y := asymmetric_type.o signature.o - - obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o - obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o -+obj-$(CONFIG_EFI_SIGNATURE_LIST_PARSER) += efi_parser.o - - # - # X.509 Certificate handling -diff --git a/crypto/asymmetric_keys/efi_parser.c b/crypto/asymmetric_keys/efi_parser.c -new file mode 100644 -index 0000000..636feb1 ---- /dev/null -+++ b/crypto/asymmetric_keys/efi_parser.c -@@ -0,0 +1,108 @@ -+/* EFI signature/key/certificate list parser -+ * -+ * Copyright (C) 2012 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) "EFI: "fmt -+#include -+#include -+#include -+#include -+#include -+ -+static __initdata efi_guid_t efi_cert_x509_guid = EFI_CERT_X509_GUID; -+ -+/** -+ * parse_efi_signature_list - Parse an EFI signature list for certificates -+ * @data: The data blob to parse -+ * @size: The size of the data blob -+ * @keyring: The keyring to add extracted keys to -+ */ -+int __init parse_efi_signature_list(const void *data, size_t size, struct key *keyring) -+{ -+ unsigned offs = 0; -+ size_t lsize, esize, hsize, elsize; -+ -+ pr_devel("-->%s(,%zu)\n", __func__, size); -+ -+ while (size > 0) { -+ efi_signature_list_t list; -+ const efi_signature_data_t *elem; -+ key_ref_t key; -+ -+ if (size < sizeof(list)) -+ return -EBADMSG; -+ -+ memcpy(&list, data, sizeof(list)); -+ pr_devel("LIST[%04x] guid=%pUl ls=%x hs=%x ss=%x\n", -+ offs, -+ list.signature_type.b, list.signature_list_size, -+ list.signature_header_size, list.signature_size); -+ -+ lsize = list.signature_list_size; -+ hsize = list.signature_header_size; -+ esize = list.signature_size; -+ elsize = lsize - sizeof(list) - hsize; -+ -+ if (lsize > size) { -+ pr_devel("<--%s() = -EBADMSG [overrun @%x]\n", -+ __func__, offs); -+ return -EBADMSG; -+ } -+ if (lsize < sizeof(list) || -+ lsize - sizeof(list) < hsize || -+ esize < sizeof(*elem) || -+ elsize < esize || -+ elsize % esize != 0) { -+ pr_devel("- bad size combo @%x\n", offs); -+ return -EBADMSG; -+ } -+ -+ if (efi_guidcmp(list.signature_type, efi_cert_x509_guid) != 0) { -+ data += lsize; -+ size -= lsize; -+ offs += lsize; -+ continue; -+ } -+ -+ data += sizeof(list) + hsize; -+ size -= sizeof(list) + hsize; -+ offs += sizeof(list) + hsize; -+ -+ for (; elsize > 0; elsize -= esize) { -+ elem = data; -+ -+ pr_devel("ELEM[%04x]\n", offs); -+ -+ key = key_create_or_update( -+ make_key_ref(keyring, 1), -+ "asymmetric", -+ NULL, -+ &elem->signature_data, -+ esize - sizeof(*elem), -+ (KEY_POS_ALL & ~KEY_POS_SETATTR) | -+ KEY_USR_VIEW, -+ KEY_ALLOC_NOT_IN_QUOTA); -+ -+ if (IS_ERR(key)) -+ pr_err("Problem loading in-kernel X.509 certificate (%ld)\n", -+ PTR_ERR(key)); -+ else -+ pr_notice("Loaded cert '%s' linked to '%s'\n", -+ key_ref_to_ptr(key)->description, -+ keyring->description); -+ -+ data += esize; -+ size -= esize; -+ offs += esize; -+ } -+ } -+ -+ return 0; -+} -diff --git a/include/linux/efi.h b/include/linux/efi.h -index 77373a7..9dab408 100644 ---- a/include/linux/efi.h -+++ b/include/linux/efi.h -@@ -619,6 +619,10 @@ extern int efi_set_rtc_mmss(unsigned long nowtime); - extern void efi_reserve_boot_services(void); - extern struct efi_memory_map memmap; - -+struct key; -+extern int __init parse_efi_signature_list(const void *data, size_t size, -+ struct key *keyring); -+ - /** - * efi_range_is_wc - check the WC bit on an address range - * @start: starting kvirt address --- -1.8.1.4 - - -From 7a798ef2fea17b56c4769c9c942d678dd8341fd3 Mon Sep 17 00:00:00 2001 -From: Josh Boyer -Date: Fri, 26 Oct 2012 12:36:24 -0400 -Subject: [PATCH 07/19] MODSIGN: Add module certificate blacklist keyring - -This adds an additional keyring that is used to store certificates that -are blacklisted. This keyring is searched first when loading signed modules -and if the module's certificate is found, it will refuse to load. This is -useful in cases where third party certificates are used for module signing. - -Signed-off-by: Josh Boyer ---- - init/Kconfig | 8 ++++++++ - kernel/modsign_pubkey.c | 14 ++++++++++++++ - kernel/module-internal.h | 3 +++ - kernel/module_signing.c | 12 ++++++++++++ - 4 files changed, 37 insertions(+) - -diff --git a/init/Kconfig b/init/Kconfig -index 5341d72..9fa68df 100644 ---- a/init/Kconfig -+++ b/init/Kconfig -@@ -1679,6 +1679,14 @@ config MODULE_SIG_ALL - comment "Do not forget to sign required modules with scripts/sign-file" - depends on MODULE_SIG_FORCE && !MODULE_SIG_ALL - -+config MODULE_SIG_BLACKLIST -+ bool "Support for blacklisting module signature certificates" -+ depends on MODULE_SIG -+ help -+ This adds support for keeping a blacklist of certificates that -+ should not pass module signature verification. If a module is -+ signed with something in this keyring, the load will be rejected. -+ - choice - prompt "Which hash algorithm should modules be signed with?" - depends on MODULE_SIG -diff --git a/kernel/modsign_pubkey.c b/kernel/modsign_pubkey.c -index 2b6e699..4cd408d 100644 ---- a/kernel/modsign_pubkey.c -+++ b/kernel/modsign_pubkey.c -@@ -17,6 +17,9 @@ - #include "module-internal.h" - - struct key *modsign_keyring; -+#ifdef CONFIG_MODULE_SIG_BLACKLIST -+struct key *modsign_blacklist; -+#endif - - extern __initdata const u8 modsign_certificate_list[]; - extern __initdata const u8 modsign_certificate_list_end[]; -@@ -43,6 +46,17 @@ static __init int module_verify_init(void) - if (IS_ERR(modsign_keyring)) - panic("Can't allocate module signing keyring\n"); - -+#ifdef CONFIG_MODULE_SIG_BLACKLIST -+ modsign_blacklist = keyring_alloc(".modsign_blacklist", -+ KUIDT_INIT(0), KGIDT_INIT(0), -+ current_cred(), -+ (KEY_POS_ALL & ~KEY_POS_SETATTR) | -+ KEY_USR_VIEW | KEY_USR_READ, -+ KEY_ALLOC_NOT_IN_QUOTA, NULL); -+ if (IS_ERR(modsign_blacklist)) -+ panic("Can't allocate module signing blacklist keyring\n"); -+#endif -+ - return 0; - } - -diff --git a/kernel/module-internal.h b/kernel/module-internal.h -index 24f9247..51a8380 100644 ---- a/kernel/module-internal.h -+++ b/kernel/module-internal.h -@@ -10,5 +10,8 @@ - */ - - extern struct key *modsign_keyring; -+#ifdef CONFIG_MODULE_SIG_BLACKLIST -+extern struct key *modsign_blacklist; -+#endif - - extern int mod_verify_sig(const void *mod, unsigned long *_modlen); -diff --git a/kernel/module_signing.c b/kernel/module_signing.c -index f2970bd..5423195 100644 ---- a/kernel/module_signing.c -+++ b/kernel/module_signing.c -@@ -157,6 +157,18 @@ static struct key *request_asymmetric_key(const char *signer, size_t signer_len, - - pr_debug("Look up: \"%s\"\n", id); - -+#ifdef CONFIG_MODULE_SIG_BLACKLIST -+ key = keyring_search(make_key_ref(modsign_blacklist, 1), -+ &key_type_asymmetric, id); -+ if (!IS_ERR(key)) { -+ /* module is signed with a cert in the blacklist. reject */ -+ pr_err("Module key '%s' is in blacklist\n", id); -+ key_ref_put(key); -+ kfree(id); -+ return ERR_PTR(-EKEYREJECTED); -+ } -+#endif -+ - key = keyring_search(make_key_ref(modsign_keyring, 1), - &key_type_asymmetric, id); - if (IS_ERR(key)) --- -1.8.1.4 - - -From a245b231b8d61f9c12c389043c77c390d0f3c23d Mon Sep 17 00:00:00 2001 -From: Josh Boyer -Date: Fri, 26 Oct 2012 12:42:16 -0400 -Subject: [PATCH 08/19] MODSIGN: Import certificates from UEFI Secure Boot - -Secure Boot stores a list of allowed certificates in the 'db' variable. -This imports those certificates into the module signing keyring. This -allows for a third party signing certificate to be used in conjunction -with signed modules. By importing the public certificate into the 'db' -variable, a user can allow a module signed with that certificate to -load. The shim UEFI bootloader has a similar certificate list stored -in the 'MokListRT' variable. We import those as well. - -In the opposite case, Secure Boot maintains a list of disallowed -certificates in the 'dbx' variable. We load those certificates into -the newly introduced module blacklist keyring and forbid any module -signed with those from loading. - -Signed-off-by: Josh Boyer ---- - include/linux/efi.h | 6 ++++ - init/Kconfig | 9 ++++++ - kernel/Makefile | 3 ++ - kernel/modsign_uefi.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++ - 4 files changed, 108 insertions(+) - create mode 100644 kernel/modsign_uefi.c - -diff --git a/include/linux/efi.h b/include/linux/efi.h -index 9dab408..b1a1809 100644 ---- a/include/linux/efi.h -+++ b/include/linux/efi.h -@@ -395,6 +395,12 @@ typedef efi_status_t efi_query_variable_store_t(u32 attributes, unsigned long si - #define EFI_CERT_X509_GUID \ - EFI_GUID( 0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72 ) - -+#define EFI_IMAGE_SECURITY_DATABASE_GUID \ -+ EFI_GUID( 0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f ) -+ -+#define EFI_SHIM_LOCK_GUID \ -+ EFI_GUID( 0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 ) -+ - typedef struct { - efi_guid_t guid; - u64 table; -diff --git a/init/Kconfig b/init/Kconfig -index 9fa68df..606295c 100644 ---- a/init/Kconfig -+++ b/init/Kconfig -@@ -1687,6 +1687,15 @@ config MODULE_SIG_BLACKLIST - should not pass module signature verification. If a module is - signed with something in this keyring, the load will be rejected. - -+config MODULE_SIG_UEFI -+ bool "Allow modules signed with certs stored in UEFI" -+ depends on MODULE_SIG && MODULE_SIG_BLACKLIST && EFI -+ select EFI_SIGNATURE_LIST_PARSER -+ help -+ This will import certificates stored in UEFI and allow modules -+ signed with those to be loaded. It will also disallow loading -+ of modules stored in the UEFI dbx variable. -+ - choice - prompt "Which hash algorithm should modules be signed with?" - depends on MODULE_SIG -diff --git a/kernel/Makefile b/kernel/Makefile -index bbde5f1..d102fb2 100644 ---- a/kernel/Makefile -+++ b/kernel/Makefile -@@ -53,6 +53,7 @@ obj-$(CONFIG_PROVE_LOCKING) += spinlock.o - obj-$(CONFIG_UID16) += uid16.o - obj-$(CONFIG_MODULES) += module.o - obj-$(CONFIG_MODULE_SIG) += module_signing.o modsign_pubkey.o modsign_certificate.o -+obj-$(CONFIG_MODULE_SIG_UEFI) += modsign_uefi.o - obj-$(CONFIG_KALLSYMS) += kallsyms.o - obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o - obj-$(CONFIG_KEXEC) += kexec.o -@@ -112,6 +113,8 @@ obj-$(CONFIG_CONTEXT_TRACKING) += context_tracking.o - - $(obj)/configs.o: $(obj)/config_data.h - -+$(obj)/modsign_uefi.o: KBUILD_CFLAGS += -fshort-wchar -+ - # config_data.h contains the same information as ikconfig.h but gzipped. - # Info from config_data can be extracted from /proc/config* - targets += config_data.gz -diff --git a/kernel/modsign_uefi.c b/kernel/modsign_uefi.c -new file mode 100644 -index 0000000..b9237d7 ---- /dev/null -+++ b/kernel/modsign_uefi.c -@@ -0,0 +1,90 @@ -+#include -+#include -+#include -+#include -+#include -+#include -+#include "module-internal.h" -+ -+static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, unsigned long *size) -+{ -+ efi_status_t status; -+ unsigned long lsize = 4; -+ unsigned long tmpdb[4]; -+ void *db = NULL; -+ -+ status = efi.get_variable(name, guid, NULL, &lsize, &tmpdb); -+ if (status != EFI_BUFFER_TOO_SMALL) { -+ pr_err("Couldn't get size: 0x%lx\n", status); -+ return NULL; -+ } -+ -+ db = kmalloc(lsize, GFP_KERNEL); -+ if (!db) { -+ pr_err("Couldn't allocate memory for uefi cert list\n"); -+ goto out; -+ } -+ -+ status = efi.get_variable(name, guid, NULL, &lsize, db); -+ if (status != EFI_SUCCESS) { -+ kfree(db); -+ db = NULL; -+ pr_err("Error reading db var: 0x%lx\n", status); -+ } -+out: -+ *size = lsize; -+ return db; -+} -+ -+/* -+ * * Load the certs contained in the UEFI databases -+ * */ -+static int __init load_uefi_certs(void) -+{ -+ efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; -+ efi_guid_t mok_var = EFI_SHIM_LOCK_GUID; -+ void *db = NULL, *dbx = NULL, *mok = NULL; -+ unsigned long dbsize = 0, dbxsize = 0, moksize = 0; -+ int rc = 0; -+ -+ /* Check if SB is enabled and just return if not */ -+ if (!efi_enabled(EFI_SECURE_BOOT)) -+ return 0; -+ -+ /* Get db, MokListRT, and dbx. They might not exist, so it isn't -+ * an error if we can't get them. -+ */ -+ db = get_cert_list(L"db", &secure_var, &dbsize); -+ if (!db) { -+ pr_err("MODSIGN: Couldn't get UEFI db list\n"); -+ } else { -+ rc = parse_efi_signature_list(db, dbsize, modsign_keyring); -+ if (rc) -+ pr_err("Couldn't parse db signatures: %d\n", rc); -+ kfree(db); -+ } -+ -+ mok = get_cert_list(L"MokListRT", &mok_var, &moksize); -+ if (!mok) { -+ pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); -+ } else { -+ rc = parse_efi_signature_list(mok, moksize, modsign_keyring); -+ if (rc) -+ pr_err("Couldn't parse MokListRT signatures: %d\n", rc); -+ kfree(mok); -+ } -+ -+ dbx = get_cert_list(L"dbx", &secure_var, &dbxsize); -+ if (!dbx) { -+ pr_info("MODSIGN: Couldn't get UEFI dbx list\n"); -+ } else { -+ rc = parse_efi_signature_list(dbx, dbxsize, -+ modsign_blacklist); -+ if (rc) -+ pr_err("Couldn't parse dbx signatures: %d\n", rc); -+ kfree(dbx); -+ } -+ -+ return rc; -+} -+late_initcall(load_uefi_certs); --- -1.8.1.4 - - -From 39e04e6106f21d0440c099fd6911863a0023f52a Mon Sep 17 00:00:00 2001 -From: Matthew Garrett -Date: Thu, 20 Sep 2012 10:40:57 -0400 -Subject: [PATCH 09/19] PCI: Lock down BAR access in secure boot environments - -Any hardware that can potentially generate DMA has to be locked down from -userspace in order to avoid it being possible for an attacker to cause -arbitrary kernel behaviour. Default to paranoid - in future we can -potentially relax this for sufficiently IOMMU-isolated devices. - -Signed-off-by: Matthew Garrett - -jwb: Temporarily work around https://bugzilla.redhat.com/show_bug.cgi?id=908888 ---- - drivers/pci/pci-sysfs.c | 16 ++++++++++++++++ - drivers/pci/proc.c | 8 +++++++- - drivers/pci/syscall.c | 2 +- - 3 files changed, 24 insertions(+), 2 deletions(-) - -diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c -index 9c6e9bb..09c311e 100644 ---- a/drivers/pci/pci-sysfs.c -+++ b/drivers/pci/pci-sysfs.c -@@ -29,6 +29,7 @@ - #include - #include - #include -+#include - #include "pci.h" - - static int sysfs_initialized; /* = 0 */ -@@ -622,6 +623,11 @@ pci_write_config(struct file* filp, struct kobject *kobj, - loff_t init_off = off; - u8 *data = (u8*) buf; - -+ /* Work around rhbz 908888 for now -+ if (!capable(CAP_COMPROMISE_KERNEL))*/ -+ if (efi_enabled(EFI_SECURE_BOOT)) -+ return -EPERM; -+ - if (off > dev->cfg_size) - return 0; - if (off + count > dev->cfg_size) { -@@ -928,6 +934,11 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, - resource_size_t start, end; - int i; - -+ /* Work around rhbz 908888 for now -+ if (!capable(CAP_COMPROMISE_KERNEL)) */ -+ if (efi_enabled(EFI_SECURE_BOOT)) -+ return -EPERM; -+ - for (i = 0; i < PCI_ROM_RESOURCE; i++) - if (res == &pdev->resource[i]) - break; -@@ -1035,6 +1046,11 @@ pci_write_resource_io(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, - loff_t off, size_t count) - { -+ /* Work around rhbz 908888 for now -+ if (!capable(CAP_COMPROMISE_KERNEL)) */ -+ if (efi_enabled(EFI_SECURE_BOOT)) -+ return -EPERM; -+ - return pci_resource_io(filp, kobj, attr, buf, off, count, true); - } - -diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c -index 0b00947..7639f68 100644 ---- a/drivers/pci/proc.c -+++ b/drivers/pci/proc.c -@@ -139,6 +139,9 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof - int size = dp->size; - int cnt; - -+ if (!capable(CAP_COMPROMISE_KERNEL)) -+ return -EPERM; -+ - if (pos >= size) - return 0; - if (nbytes >= size) -@@ -219,6 +222,9 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd, - #endif /* HAVE_PCI_MMAP */ - int ret = 0; - -+ if (!capable(CAP_COMPROMISE_KERNEL)) -+ return -EPERM; -+ - switch (cmd) { - case PCIIOC_CONTROLLER: - ret = pci_domain_nr(dev->bus); -@@ -259,7 +265,7 @@ static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma) - struct pci_filp_private *fpriv = file->private_data; - int i, ret; - -- if (!capable(CAP_SYS_RAWIO)) -+ if (!capable(CAP_SYS_RAWIO) || !capable(CAP_COMPROMISE_KERNEL)) - return -EPERM; - - /* Make sure the caller is mapping a real resource for this device */ -diff --git a/drivers/pci/syscall.c b/drivers/pci/syscall.c -index e1c1ec5..97e785f 100644 ---- a/drivers/pci/syscall.c -+++ b/drivers/pci/syscall.c -@@ -92,7 +92,7 @@ SYSCALL_DEFINE5(pciconfig_write, unsigned long, bus, unsigned long, dfn, - u32 dword; - int err = 0; - -- if (!capable(CAP_SYS_ADMIN)) -+ if (!capable(CAP_SYS_ADMIN) || !capable(CAP_COMPROMISE_KERNEL)) - return -EPERM; - - dev = pci_get_bus_and_slot(bus, dfn); --- -1.8.1.4 - - -From 45e3db2ab117829efd167f96487ea7d6f9e1ea6d Mon Sep 17 00:00:00 2001 -From: Matthew Garrett -Date: Thu, 20 Sep 2012 10:40:58 -0400 -Subject: [PATCH 10/19] x86: Lock down IO port access in secure boot - environments - -IO port access would permit users to gain access to PCI configuration -registers, which in turn (on a lot of hardware) give access to MMIO register -space. This would potentially permit root to trigger arbitrary DMA, so lock -it down by default. - -Signed-off-by: Matthew Garrett ---- - arch/x86/kernel/ioport.c | 4 ++-- - drivers/char/mem.c | 3 +++ - 2 files changed, 5 insertions(+), 2 deletions(-) - -diff --git a/arch/x86/kernel/ioport.c b/arch/x86/kernel/ioport.c -index 4ddaf66..f505995 100644 ---- a/arch/x86/kernel/ioport.c -+++ b/arch/x86/kernel/ioport.c -@@ -28,7 +28,7 @@ asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int turn_on) - - if ((from + num <= from) || (from + num > IO_BITMAP_BITS)) - return -EINVAL; -- if (turn_on && !capable(CAP_SYS_RAWIO)) -+ if (turn_on && (!capable(CAP_SYS_RAWIO) || !capable(CAP_COMPROMISE_KERNEL))) - return -EPERM; - - /* -@@ -103,7 +103,7 @@ SYSCALL_DEFINE1(iopl, unsigned int, level) - return -EINVAL; - /* Trying to gain more privileges? */ - if (level > old) { -- if (!capable(CAP_SYS_RAWIO)) -+ if (!capable(CAP_SYS_RAWIO) || !capable(CAP_COMPROMISE_KERNEL)) - return -EPERM; - } - regs->flags = (regs->flags & ~X86_EFLAGS_IOPL) | (level << 12); -diff --git a/drivers/char/mem.c b/drivers/char/mem.c -index 2c644af..7eee4d8 100644 ---- a/drivers/char/mem.c -+++ b/drivers/char/mem.c -@@ -597,6 +597,9 @@ static ssize_t write_port(struct file *file, const char __user *buf, - unsigned long i = *ppos; - const char __user *tmp = buf; - -+ if (!capable(CAP_COMPROMISE_KERNEL)) -+ return -EPERM; -+ - if (!access_ok(VERIFY_READ, buf, count)) - return -EFAULT; - while (count-- > 0 && i < 65536) { --- -1.8.1.4 - - -From 62cc8b181cd49e047f4999a3e4f65a4f222fb2c9 Mon Sep 17 00:00:00 2001 -From: Matthew Garrett -Date: Thu, 20 Sep 2012 10:40:59 -0400 -Subject: [PATCH 11/19] ACPI: Limit access to custom_method - -It must be impossible for even root to get code executed in kernel context -under a secure boot environment. custom_method effectively allows arbitrary -access to system memory, so it needs to have a capability check here. - -Signed-off-by: Matthew Garrett ---- - drivers/acpi/custom_method.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/drivers/acpi/custom_method.c b/drivers/acpi/custom_method.c -index 12b62f2..edf0710 100644 ---- a/drivers/acpi/custom_method.c -+++ b/drivers/acpi/custom_method.c -@@ -29,6 +29,9 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf, - struct acpi_table_header table; - acpi_status status; - -+ if (!capable(CAP_COMPROMISE_KERNEL)) -+ return -EPERM; -+ - if (!(*ppos)) { - /* parse the table header to get the table length */ - if (count <= sizeof(struct acpi_table_header)) --- -1.8.1.4 - - -From b11830a03ddfa478ad5a73a88067a2a7d55dce5b Mon Sep 17 00:00:00 2001 -From: Matthew Garrett -Date: Thu, 20 Sep 2012 10:41:00 -0400 -Subject: [PATCH 12/19] asus-wmi: Restrict debugfs interface - -We have no way of validating what all of the Asus WMI methods do on a -given machine, and there's a risk that some will allow hardware state to -be manipulated in such a way that arbitrary code can be executed in the -kernel. Add a capability check to prevent that. - -Signed-off-by: Matthew Garrett ---- - drivers/platform/x86/asus-wmi.c | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c -index c11b242..6d5f88f 100644 ---- a/drivers/platform/x86/asus-wmi.c -+++ b/drivers/platform/x86/asus-wmi.c -@@ -1617,6 +1617,9 @@ static int show_dsts(struct seq_file *m, void *data) - int err; - u32 retval = -1; - -+ if (!capable(CAP_COMPROMISE_KERNEL)) -+ return -EPERM; -+ - err = asus_wmi_get_devstate(asus, asus->debug.dev_id, &retval); - - if (err < 0) -@@ -1633,6 +1636,9 @@ static int show_devs(struct seq_file *m, void *data) - int err; - u32 retval = -1; - -+ if (!capable(CAP_COMPROMISE_KERNEL)) -+ return -EPERM; -+ - err = asus_wmi_set_devstate(asus->debug.dev_id, asus->debug.ctrl_param, - &retval); - -@@ -1657,6 +1663,9 @@ static int show_call(struct seq_file *m, void *data) - union acpi_object *obj; - acpi_status status; - -+ if (!capable(CAP_COMPROMISE_KERNEL)) -+ return -EPERM; -+ - status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, - 1, asus->debug.method_id, - &input, &output); --- -1.8.1.4 - - -From 0d64b3972b75e420cddde5ba5c85f98046774450 Mon Sep 17 00:00:00 2001 -From: Matthew Garrett -Date: Thu, 20 Sep 2012 10:41:01 -0400 -Subject: [PATCH 13/19] Restrict /dev/mem and /dev/kmem in secure boot setups - -Allowing users to write to address space makes it possible for the kernel -to be subverted. Restrict this when we need to protect the kernel. - -Signed-off-by: Matthew Garrett ---- - drivers/char/mem.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/drivers/char/mem.c b/drivers/char/mem.c -index 7eee4d8..772ee2b 100644 ---- a/drivers/char/mem.c -+++ b/drivers/char/mem.c -@@ -158,6 +158,9 @@ static ssize_t write_mem(struct file *file, const char __user *buf, - unsigned long copied; - void *ptr; - -+ if (!capable(CAP_COMPROMISE_KERNEL)) -+ return -EPERM; -+ - if (!valid_phys_addr_range(p, count)) - return -EFAULT; - -@@ -530,6 +533,9 @@ static ssize_t write_kmem(struct file *file, const char __user *buf, - char *kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */ - int err = 0; - -+ if (!capable(CAP_COMPROMISE_KERNEL)) -+ return -EPERM; -+ - if (p < (unsigned long) high_memory) { - unsigned long to_write = min_t(unsigned long, count, - (unsigned long)high_memory - p); --- -1.8.1.4 - - -From 9f39d364fb1b8270a85ad9541710d4a7dd3bc0b5 Mon Sep 17 00:00:00 2001 -From: Josh Boyer -Date: Thu, 20 Sep 2012 10:41:04 -0400 -Subject: [PATCH 14/19] acpi: Ignore acpi_rsdp kernel parameter in a secure - boot environment - -This option allows userspace to pass the RSDP address to the kernel. This -could potentially be used to circumvent the secure boot trust model. -This is setup through the setup_arch function, which is called before the -security_init function sets up the security_ops, so we cannot use a -capable call here. We ignore the setting if we are booted in Secure Boot -mode. - -Signed-off-by: Josh Boyer ---- - drivers/acpi/osl.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c -index 586e7e9..8950454 100644 ---- a/drivers/acpi/osl.c -+++ b/drivers/acpi/osl.c -@@ -245,7 +245,7 @@ early_param("acpi_rsdp", setup_acpi_rsdp); - acpi_physical_address __init acpi_os_get_root_pointer(void) - { - #ifdef CONFIG_KEXEC -- if (acpi_rsdp) -+ if (acpi_rsdp && !efi_enabled(EFI_SECURE_BOOT)) - return acpi_rsdp; - #endif - --- -1.8.1.4 - - -From c06f86d634932ceb788e9d883346e2d5e4117f52 Mon Sep 17 00:00:00 2001 -From: Matthew Garrett -Date: Tue, 4 Sep 2012 11:55:13 -0400 -Subject: [PATCH 15/19] kexec: Disable in a secure boot environment - -kexec could be used as a vector for a malicious user to use a signed kernel -to circumvent the secure boot trust model. In the long run we'll want to -support signed kexec payloads, but for the moment we should just disable -loading entirely in that situation. - -Signed-off-by: Matthew Garrett ---- - kernel/kexec.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/kernel/kexec.c b/kernel/kexec.c -index ffd4e11..e276744 100644 ---- a/kernel/kexec.c -+++ b/kernel/kexec.c -@@ -946,7 +946,7 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments, - int result; - - /* We only trust the superuser with rebooting the system. */ -- if (!capable(CAP_SYS_BOOT)) -+ if (!capable(CAP_SYS_BOOT) || !capable(CAP_COMPROMISE_KERNEL)) - return -EPERM; - - /* --- -1.8.1.4 - - -From 3555227817a9adbed51995104f4aeae4c6c36be9 Mon Sep 17 00:00:00 2001 -From: Josh Boyer -Date: Fri, 5 Oct 2012 10:12:48 -0400 -Subject: [PATCH 16/19] MODSIGN: Always enforce module signing in a Secure Boot - environment - -If a machine is booted into a Secure Boot environment, we need to -protect the trust model. This requires that all modules be signed -with a key that is in the kernel's _modsign keyring. The checks for -this are already done via the 'sig_enforce' module parameter. Make -this visible within the kernel and force it to be true. - -Signed-off-by: Josh Boyer ---- - kernel/cred.c | 8 ++++++++ - kernel/module.c | 4 ++-- - 2 files changed, 10 insertions(+), 2 deletions(-) - -diff --git a/kernel/cred.c b/kernel/cred.c -index c3f4e3e..c5554e0 100644 ---- a/kernel/cred.c -+++ b/kernel/cred.c -@@ -565,11 +565,19 @@ void __init cred_init(void) - 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); - } - -+#ifdef CONFIG_MODULE_SIG -+extern bool sig_enforce; -+#endif -+ - void __init secureboot_enable() - { - pr_info("Secure boot enabled\n"); - cap_lower((&init_cred)->cap_bset, CAP_COMPROMISE_KERNEL); - cap_lower((&init_cred)->cap_permitted, CAP_COMPROMISE_KERNEL); -+#ifdef CONFIG_MODULE_SIG -+ /* Enable module signature enforcing */ -+ sig_enforce = true; -+#endif - } - - /* Dummy Secure Boot enable option to fake out UEFI SB=1 */ -diff --git a/kernel/module.c b/kernel/module.c -index 0925c9a..af4a476 100644 ---- a/kernel/module.c -+++ b/kernel/module.c -@@ -109,9 +109,9 @@ struct list_head *kdb_modules = &modules; /* kdb needs the list of modules */ - - #ifdef CONFIG_MODULE_SIG - #ifdef CONFIG_MODULE_SIG_FORCE --static bool sig_enforce = true; -+bool sig_enforce = true; - #else --static bool sig_enforce = false; -+bool sig_enforce = false; - - static int param_set_bool_enable_only(const char *val, - const struct kernel_param *kp) --- -1.8.1.4 - - -From 844991aad05c36af54bf4540a71e2f5430da444f Mon Sep 17 00:00:00 2001 -From: Josh Boyer -Date: Fri, 26 Oct 2012 14:02:09 -0400 -Subject: [PATCH 17/19] hibernate: Disable in a Secure Boot environment - -There is currently no way to verify the resume image when returning -from hibernate. This might compromise the secure boot trust model, -so until we can work with signed hibernate images we disable it in -a Secure Boot environment. - -Signed-off-by: Josh Boyer ---- - kernel/power/hibernate.c | 15 ++++++++++++++- - kernel/power/main.c | 7 ++++++- - kernel/power/user.c | 3 +++ - 3 files changed, 23 insertions(+), 2 deletions(-) - -diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c -index b26f5f1..7f63cb4 100644 ---- a/kernel/power/hibernate.c -+++ b/kernel/power/hibernate.c -@@ -28,6 +28,7 @@ - #include - #include - #include -+#include - - #include "power.h" - -@@ -632,6 +633,10 @@ int hibernate(void) - { - int error; - -+ if (!capable(CAP_COMPROMISE_KERNEL)) { -+ return -EPERM; -+ } -+ - lock_system_sleep(); - /* The snapshot device should not be opened while we're running */ - if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { -@@ -723,7 +728,7 @@ static int software_resume(void) - /* - * If the user said "noresume".. bail out early. - */ -- if (noresume) -+ if (noresume || !capable(CAP_COMPROMISE_KERNEL)) - return 0; - - /* -@@ -889,6 +894,11 @@ static ssize_t disk_show(struct kobject *kobj, struct kobj_attribute *attr, - int i; - char *start = buf; - -+ if (efi_enabled(EFI_SECURE_BOOT)) { -+ buf += sprintf(buf, "[%s]\n", "disabled"); -+ return buf-start; -+ } -+ - for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) { - if (!hibernation_modes[i]) - continue; -@@ -923,6 +933,9 @@ static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr, - char *p; - int mode = HIBERNATION_INVALID; - -+ if (!capable(CAP_COMPROMISE_KERNEL)) -+ return -EPERM; -+ - p = memchr(buf, '\n', n); - len = p ? p - buf : n; - -diff --git a/kernel/power/main.c b/kernel/power/main.c -index d77663b..78f8ed5 100644 ---- a/kernel/power/main.c -+++ b/kernel/power/main.c -@@ -15,6 +15,7 @@ - #include - #include - #include -+#include - - #include "power.h" - -@@ -301,7 +302,11 @@ static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr, - } - #endif - #ifdef CONFIG_HIBERNATION -- s += sprintf(s, "%s\n", "disk"); -+ if (!efi_enabled(EFI_SECURE_BOOT)) { -+ s += sprintf(s, "%s\n", "disk"); -+ } else { -+ s += sprintf(s, "\n"); -+ } - #else - if (s != buf) - /* convert the last space to a newline */ -diff --git a/kernel/power/user.c b/kernel/power/user.c -index 4ed81e7..b11a0f4 100644 ---- a/kernel/power/user.c -+++ b/kernel/power/user.c -@@ -48,6 +48,9 @@ static int snapshot_open(struct inode *inode, struct file *filp) - struct snapshot_data *data; - int error; - -+ if (!capable(CAP_COMPROMISE_KERNEL)) -+ return -EPERM; -+ - lock_system_sleep(); - - if (!atomic_add_unless(&snapshot_device_available, -1, 0)) { --- -1.8.1.4 - - -From 6f6d20668d3294e6a29d01a13a7415a542cb6413 Mon Sep 17 00:00:00 2001 -From: Josh Boyer -Date: Tue, 5 Feb 2013 19:25:05 -0500 -Subject: [PATCH 18/19] efi: Disable secure boot if shim is in insecure mode - -A user can manually tell the shim boot loader to disable validation of -images it loads. When a user does this, it creates a UEFI variable called -MokSBState that does not have the runtime attribute set. Given that the -user explicitly disabled validation, we can honor that and not enable -secure boot mode if that variable is set. - -Signed-off-by: Josh Boyer ---- - arch/x86/boot/compressed/eboot.c | 20 +++++++++++++++++++- - 1 file changed, 19 insertions(+), 1 deletion(-) - -diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c -index 0998ec7..4945ee5 100644 ---- a/arch/x86/boot/compressed/eboot.c -+++ b/arch/x86/boot/compressed/eboot.c -@@ -908,8 +908,9 @@ fail: - - static int get_secure_boot(efi_system_table_t *_table) - { -- u8 sb, setup; -+ u8 sb, setup, moksbstate; - unsigned long datasize = sizeof(sb); -+ u32 attr; - efi_guid_t var_guid = EFI_GLOBAL_VARIABLE_GUID; - efi_status_t status; - -@@ -933,6 +934,23 @@ static int get_secure_boot(efi_system_table_t *_table) - if (setup == 1) - return 0; - -+ /* See if a user has put shim into insecure_mode. If so, and the variable -+ * doesn't have the runtime attribute set, we might as well honor that. -+ */ -+ var_guid = EFI_SHIM_LOCK_GUID; -+ status = efi_call_phys5(sys_table->runtime->get_variable, -+ L"MokSBState", &var_guid, &attr, &datasize, -+ &moksbstate); -+ -+ /* If it fails, we don't care why. Default to secure */ -+ if (status != EFI_SUCCESS) -+ return 1; -+ -+ if (!(attr & EFI_VARIABLE_RUNTIME_ACCESS)) { -+ if (moksbstate == 1) -+ return 0; -+ } -+ - return 1; - } - --- -1.8.1.4 - - -From 53afc5a04d5b0fbdad1e4a34fbbecfdb5ff2f6dd Mon Sep 17 00:00:00 2001 -From: Kees Cook -Date: Fri, 8 Feb 2013 11:12:13 -0800 -Subject: [PATCH 19/19] x86: Lock down MSR writing in secure boot - -Writing to MSRs should not be allowed unless CAP_COMPROMISE_KERNEL is -set since it could lead to execution of arbitrary code in kernel mode. - -Signed-off-by: Kees Cook ---- - arch/x86/kernel/msr.c | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/arch/x86/kernel/msr.c b/arch/x86/kernel/msr.c -index ce13049..fa4dc6c 100644 ---- a/arch/x86/kernel/msr.c -+++ b/arch/x86/kernel/msr.c -@@ -103,6 +103,9 @@ static ssize_t msr_write(struct file *file, const char __user *buf, - int err = 0; - ssize_t bytes = 0; - -+ if (!capable(CAP_COMPROMISE_KERNEL)) -+ return -EPERM; -+ - if (count % 8) - return -EINVAL; /* Invalid chunk size */ - -@@ -150,6 +153,10 @@ static long msr_ioctl(struct file *file, unsigned int ioc, unsigned long arg) - err = -EBADF; - break; - } -+ if (!capable(CAP_COMPROMISE_KERNEL)) { -+ err = -EPERM; -+ break; -+ } - if (copy_from_user(®s, uregs, sizeof regs)) { - err = -EFAULT; - break; --- -1.8.1.4 - diff --git a/sources b/sources index 5947c8bcb..a87e5a10a 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -4348c9b6b2eb3144d601e87c19d5d909 linux-3.9.tar.xz -552146435b7ecc414bf8e3cd8bb6ac4a patch-3.9.11.xz +4f25cd5bec5f8d5a7d935b3f2ccb8481 linux-3.10.tar.xz +2e46ab138670b3171b52b849568cb42f patch-3.10.4.xz diff --git a/vhost-net-fix-use-after-free-in-vhost_net_flush.patch b/vhost-net-fix-use-after-free-in-vhost_net_flush.patch deleted file mode 100644 index f9a6a7b9f..000000000 --- a/vhost-net-fix-use-after-free-in-vhost_net_flush.patch +++ /dev/null @@ -1,76 +0,0 @@ -From 0c9d7f6ea817d5328a09a78e901b16e1836ca4d7 Mon Sep 17 00:00:00 2001 -From: "Michael S. Tsirkin" -Date: Tue, 25 Jun 2013 17:29:46 +0300 -Subject: [PATCH] vhost-net: fix use-after-free in vhost_net_flush - -vhost_net_ubuf_put_and_wait has a confusing name: -it will actually also free it's argument. -Thus since commit 1280c27f8e29acf4af2da914e80ec27c3dbd5c01 - "vhost-net: flush outstanding DMAs on memory change" -vhost_net_flush tries to use the argument after passing it -to vhost_net_ubuf_put_and_wait, this results -in use after free. -To fix, don't free the argument in vhost_net_ubuf_put_and_wait, -add an new API for callers that want to free ubufs. - -Acked-by: Asias He -Acked-by: Jason Wang -Signed-off-by: Michael S. Tsirkin ---- - drivers/vhost/net.c | 4 ++-- - drivers/vhost/vhost.c | 5 +++++ - drivers/vhost/vhost.h | 1 + - 3 files changed, 8 insertions(+), 2 deletions(-) - -diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c -index dfff647..d8d4f57 100644 ---- a/drivers/vhost/net.c -+++ b/drivers/vhost/net.c -@@ -857,7 +857,7 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd) - mutex_unlock(&vq->mutex); - - if (oldubufs) { -- vhost_ubuf_put_and_wait(oldubufs); -+ vhost_ubuf_put_wait_and_free(oldubufs); - mutex_lock(&vq->mutex); - vhost_zerocopy_signal_used(n, vq); - mutex_unlock(&vq->mutex); -@@ -875,7 +875,7 @@ err_used: - rcu_assign_pointer(vq->private_data, oldsock); - vhost_net_enable_vq(n, vq); - if (ubufs) -- vhost_ubuf_put_and_wait(ubufs); -+ vhost_ubuf_put_wait_and_free(ubufs); - err_ubufs: - fput(sock->file); - err_vq: -diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c -index 9759249..ff53c9e 100644 ---- a/drivers/vhost/vhost.c -+++ b/drivers/vhost/vhost.c -@@ -1581,5 +1581,10 @@ void vhost_ubuf_put_and_wait(struct vhost_ubuf_ref *ubufs) - { - kref_put(&ubufs->kref, vhost_zerocopy_done_signal); - wait_event(ubufs->wait, !atomic_read(&ubufs->kref.refcount)); -+} -+ -+void vhost_ubuf_put_wait_and_free(struct vhost_ubuf_ref *ubufs) -+{ -+ vhost_ubuf_put_and_wait(ubufs); - kfree(ubufs); - } -diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h -index 17261e2..dd63b35 100644 ---- a/drivers/vhost/vhost.h -+++ b/drivers/vhost/vhost.h -@@ -63,6 +63,7 @@ struct vhost_ubuf_ref { - struct vhost_ubuf_ref *vhost_ubuf_alloc(struct vhost_virtqueue *, bool zcopy); - void vhost_ubuf_put(struct vhost_ubuf_ref *); - void vhost_ubuf_put_and_wait(struct vhost_ubuf_ref *); -+void vhost_ubuf_put_wait_and_free(struct vhost_ubuf_ref *); - - struct ubuf_info; - --- -1.8.2.1 -