From 016e29c0971ccc3e0f5b81a4888ea985731494af Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Thu, 25 Aug 2016 08:32:34 +0200 Subject: [PATCH] watchdog: mei_wdt: implement MEI iAMT watchdog driver --- Makefile | 5 + changelog.Debian | 2 + mei_bus-whitelist-watchdog-client.patch | 74 +++ mei_drop-watchdog-code.patch | 754 +++++++++++++++++++++++ watchdog_implement-mei-iamt-driver.patch | 514 +++++++++++++++ 5 files changed, 1349 insertions(+) create mode 100644 mei_bus-whitelist-watchdog-client.patch create mode 100644 mei_drop-watchdog-code.patch create mode 100644 watchdog_implement-mei-iamt-driver.patch diff --git a/Makefile b/Makefile index c663874..0c91107 100644 --- a/Makefile +++ b/Makefile @@ -202,6 +202,7 @@ data: .compile_mark igb.ko ixgbe.ko e1000e.ko ${SPL_MODULES} ${ZFS_MODULES} ${DR mv tmp data PVE_CONFIG_OPTS= \ +-m INTEL_MEI_WDT \ -d CONFIG_SND_PCM_OSS \ -d CONFIG_TRANSPARENT_HUGEPAGE_MADVISE \ -d CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS \ @@ -256,6 +257,10 @@ ${KERNEL_SRC}/README ${KERNEL_CFG_ORG}: ${KERNELSRCTAR} cd ${KERNEL_SRC}; patch -p1 < ../981-2-PCI-Quirk-PCH-root-port-ACS-for-Sunrise-Point.patch cd ${KERNEL_SRC}; patch -p1 < ../kvm-dynamic-halt-polling-disable-default.patch cd ${KERNEL_SRC}; patch -p1 < ../ie31200_edac-add-skylake-support.patch + # avoid iAMT watchdog problems (not a real watchdog, because does not reset the host) + cd ${KERNEL_SRC}; patch -p1 < ../watchdog_implement-mei-iamt-driver.patch + cd ${KERNEL_SRC}; patch -p1 < ../mei_drop-watchdog-code.patch + cd ${KERNEL_SRC}; patch -p1 < ../mei_bus-whitelist-watchdog-client.patch sed -i ${KERNEL_SRC}/Makefile -e 's/^EXTRAVERSION.*$$/EXTRAVERSION=${EXTRAVERSION}/' touch $@ diff --git a/changelog.Debian b/changelog.Debian index c6d9dbf..d24e91e 100644 --- a/changelog.Debian +++ b/changelog.Debian @@ -10,6 +10,8 @@ pve-kernel (4.4.16-61) unstable; urgency=medium * update igb to 5.3.5.3 + * watchdog: mei_wdt: implement MEI iAMT watchdog driver + -- Proxmox Support Team Wed, 24 Aug 2016 15:08:27 +0200 pve-kernel (4.4.15-60) unstable; urgency=medium diff --git a/mei_bus-whitelist-watchdog-client.patch b/mei_bus-whitelist-watchdog-client.patch new file mode 100644 index 0000000..5870e51 --- /dev/null +++ b/mei_bus-whitelist-watchdog-client.patch @@ -0,0 +1,74 @@ +From e97cdb303c04bfe60283094bdced68a0d363bd5d Mon Sep 17 00:00:00 2001 +From: Tomas Winkler +Date: Fri, 8 Jan 2016 00:49:24 +0200 +Subject: mei: bus: whitelist the watchdog client + +The iAMT WD client has to be whitelisted sice it has two connections +and is filtered out by number_of_connections fixup. +Also the API has changed for BDW and SKL but firmware haven't updated +the protocol version. + +Signed-off-by: Tomas Winkler +Signed-off-by: Alexander Usyskin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/misc/mei/bus-fixup.c | 29 +++++++++++++++++++++++++++++ + 1 file changed, 29 insertions(+) + +diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c +index b2d2a6e..b87323f 100644 +--- a/drivers/misc/mei/bus-fixup.c ++++ b/drivers/misc/mei/bus-fixup.c +@@ -35,6 +35,9 @@ static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO; + #define MEI_UUID_NFC_HCI UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, \ + 0x94, 0xd4, 0x50, 0x26, 0x67, 0x23, 0x77, 0x5c) + ++#define MEI_UUID_WD UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, \ ++ 0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB) ++ + #define MEI_UUID_ANY NULL_UUID_LE + + /** +@@ -66,6 +69,31 @@ static void blacklist(struct mei_cl_device *cldev) + cldev->do_match = 0; + } + ++/** ++ * mei_wd - wd client on the bus, change protocol version ++ * as the API has changed. ++ * ++ * @cldev: me clients device ++ */ ++#if IS_ENABLED(CONFIG_INTEL_MEI_ME) ++#include ++#include "hw-me-regs.h" ++static void mei_wd(struct mei_cl_device *cldev) ++{ ++ struct pci_dev *pdev = to_pci_dev(cldev->dev.parent); ++ ++ dev_dbg(&cldev->dev, "running hook %s\n", __func__); ++ if (pdev->device == MEI_DEV_ID_WPT_LP || ++ pdev->device == MEI_DEV_ID_SPT || ++ pdev->device == MEI_DEV_ID_SPT_H) ++ cldev->me_cl->props.protocol_version = 0x2; ++ ++ cldev->do_match = 1; ++} ++#else ++static inline void mei_wd(struct mei_cl_device *cldev) {} ++#endif /* CONFIG_INTEL_MEI_ME */ ++ + struct mei_nfc_cmd { + u8 command; + u8 status; +@@ -280,6 +308,7 @@ static struct mei_fixup { + MEI_FIXUP(MEI_UUID_ANY, number_of_connections), + MEI_FIXUP(MEI_UUID_NFC_INFO, blacklist), + MEI_FIXUP(MEI_UUID_NFC_HCI, mei_nfc), ++ MEI_FIXUP(MEI_UUID_WD, mei_wd), + }; + + /** +-- +cgit v0.12 + diff --git a/mei_drop-watchdog-code.patch b/mei_drop-watchdog-code.patch new file mode 100644 index 0000000..f33aad2 --- /dev/null +++ b/mei_drop-watchdog-code.patch @@ -0,0 +1,754 @@ +From fdd9b8655933c3eb3154fe1ed351c17b654258bd Mon Sep 17 00:00:00 2001 +From: Alexander Usyskin +Date: Fri, 8 Jan 2016 00:49:21 +0200 +Subject: mei: wd: drop the watchdog code from the core mei driver + +Instead of integrating the iAMT watchdog in the mei core driver +we will create a watchdog device on the mei client bus and +create a driver for it. + +This patch removes the watchdog code from the mei core driver. + +Signed-off-by: Alexander Usyskin +Signed-off-by: Tomas Winkler +Signed-off-by: Greg Kroah-Hartman +--- + drivers/misc/mei/Kconfig | 6 +- + drivers/misc/mei/Makefile | 1 - + drivers/misc/mei/client.c | 12 +- + drivers/misc/mei/client.h | 4 - + drivers/misc/mei/init.c | 10 +- + drivers/misc/mei/interrupt.c | 15 -- + drivers/misc/mei/mei_dev.h | 61 +------ + drivers/misc/mei/wd.c | 391 ------------------------------------------- + 8 files changed, 9 insertions(+), 491 deletions(-) + delete mode 100644 drivers/misc/mei/wd.c + +diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig +index d23384d..c49e1d2 100644 +--- a/drivers/misc/mei/Kconfig ++++ b/drivers/misc/mei/Kconfig +@@ -1,6 +1,6 @@ + config INTEL_MEI + tristate "Intel Management Engine Interface" +- depends on X86 && PCI && WATCHDOG_CORE ++ depends on X86 && PCI + help + The Intel Management Engine (Intel ME) provides Manageability, + Security and Media services for system containing Intel chipsets. +@@ -12,7 +12,7 @@ config INTEL_MEI + config INTEL_MEI_ME + tristate "ME Enabled Intel Chipsets" + select INTEL_MEI +- depends on X86 && PCI && WATCHDOG_CORE ++ depends on X86 && PCI + help + MEI support for ME Enabled Intel chipsets. + +@@ -37,7 +37,7 @@ config INTEL_MEI_ME + config INTEL_MEI_TXE + tristate "Intel Trusted Execution Environment with ME Interface" + select INTEL_MEI +- depends on X86 && PCI && WATCHDOG_CORE ++ depends on X86 && PCI + help + MEI Support for Trusted Execution Environment device on Intel SoCs + +diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile +index 01447ca..59e6b0a 100644 +--- a/drivers/misc/mei/Makefile ++++ b/drivers/misc/mei/Makefile +@@ -9,7 +9,6 @@ mei-objs += interrupt.o + mei-objs += client.o + mei-objs += main.o + mei-objs += amthif.o +-mei-objs += wd.o + mei-objs += bus.o + mei-objs += bus-fixup.o + mei-$(CONFIG_DEBUG_FS) += debugfs.o +diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c +index 72e3261..e069fca 100644 +--- a/drivers/misc/mei/client.c ++++ b/drivers/misc/mei/client.c +@@ -648,7 +648,7 @@ int mei_cl_unlink(struct mei_cl *cl) + if (!cl) + return 0; + +- /* wd and amthif might not be initialized */ ++ /* amthif might not be initialized */ + if (!cl->dev) + return 0; + +@@ -679,17 +679,11 @@ void mei_host_client_init(struct work_struct *work) + + mutex_lock(&dev->device_lock); + +- + me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid); + if (me_cl) + mei_amthif_host_init(dev, me_cl); + mei_me_cl_put(me_cl); + +- me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid); +- if (me_cl) +- mei_wd_host_init(dev, me_cl); +- mei_me_cl_put(me_cl); +- + dev->dev_state = MEI_DEV_ENABLED; + dev->reset_count = 0; + mutex_unlock(&dev->device_lock); +@@ -1153,7 +1147,7 @@ err: + * + * Return: 1 if mei_flow_ctrl_creds >0, 0 - otherwise. + */ +-int mei_cl_flow_ctrl_creds(struct mei_cl *cl) ++static int mei_cl_flow_ctrl_creds(struct mei_cl *cl) + { + int rets; + +@@ -1186,7 +1180,7 @@ int mei_cl_flow_ctrl_creds(struct mei_cl *cl) + * 0 on success + * -EINVAL when ctrl credits are <= 0 + */ +-int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) ++static int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) + { + if (WARN_ON(!cl || !cl->me_cl)) + return -EINVAL; +diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h +index 04e1aa3..2e90a25 100644 +--- a/drivers/misc/mei/client.h ++++ b/drivers/misc/mei/client.h +@@ -18,7 +18,6 @@ + #define _MEI_CLIENT_H_ + + #include +-#include + #include + #include + +@@ -120,9 +119,6 @@ struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length, + enum mei_cb_file_ops type, struct file *fp); + int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp); + +-int mei_cl_flow_ctrl_creds(struct mei_cl *cl); +- +-int mei_cl_flow_ctrl_reduce(struct mei_cl *cl); + /* + * MEI input output function prototype + */ +diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c +index 3edafc8..46a4302 100644 +--- a/drivers/misc/mei/init.c ++++ b/drivers/misc/mei/init.c +@@ -156,8 +156,7 @@ int mei_reset(struct mei_device *dev) + mei_cl_all_wakeup(dev); + + /* remove entry if already in list */ +- dev_dbg(dev->dev, "remove iamthif and wd from the file list.\n"); +- mei_cl_unlink(&dev->wd_cl); ++ dev_dbg(dev->dev, "remove iamthif from the file list.\n"); + mei_cl_unlink(&dev->iamthif_cl); + mei_amthif_reset_params(dev); + } +@@ -165,7 +164,6 @@ int mei_reset(struct mei_device *dev) + mei_hbm_reset(dev); + + dev->rd_msg_hdr = 0; +- dev->wd_pending = false; + + if (ret) { + dev_err(dev->dev, "hw_reset failed ret = %d\n", ret); +@@ -335,16 +333,12 @@ void mei_stop(struct mei_device *dev) + + mutex_lock(&dev->device_lock); + +- mei_wd_stop(dev); +- + dev->dev_state = MEI_DEV_POWER_DOWN; + mei_reset(dev); + /* move device to disabled state unconditionally */ + dev->dev_state = MEI_DEV_DISABLED; + + mutex_unlock(&dev->device_lock); +- +- mei_watchdog_unregister(dev); + } + EXPORT_SYMBOL_GPL(mei_stop); + +@@ -394,7 +388,6 @@ void mei_device_init(struct mei_device *dev, + init_waitqueue_head(&dev->wait_hw_ready); + init_waitqueue_head(&dev->wait_pg); + init_waitqueue_head(&dev->wait_hbm_start); +- init_waitqueue_head(&dev->wait_stop_wd); + dev->dev_state = MEI_DEV_INITIALIZING; + dev->reset_count = 0; + +@@ -407,7 +400,6 @@ void mei_device_init(struct mei_device *dev, + INIT_WORK(&dev->init_work, mei_host_client_init); + INIT_WORK(&dev->reset_work, mei_reset_work); + +- INIT_LIST_HEAD(&dev->wd_cl.link); + INIT_LIST_HEAD(&dev->iamthif_cl.link); + mei_io_list_init(&dev->amthif_cmd_list); + mei_io_list_init(&dev->amthif_rd_complete_list); +diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c +index 64b568a..6340dee 100644 +--- a/drivers/misc/mei/interrupt.c ++++ b/drivers/misc/mei/interrupt.c +@@ -360,21 +360,6 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) + list_move_tail(&cb->list, &cmpl_list->list); + } + +- if (dev->wd_state == MEI_WD_STOPPING) { +- dev->wd_state = MEI_WD_IDLE; +- wake_up(&dev->wait_stop_wd); +- } +- +- if (mei_cl_is_connected(&dev->wd_cl)) { +- if (dev->wd_pending && +- mei_cl_flow_ctrl_creds(&dev->wd_cl) > 0) { +- ret = mei_wd_send(dev); +- if (ret) +- return ret; +- dev->wd_pending = false; +- } +- } +- + /* complete control write list CB */ + dev_dbg(dev->dev, "complete control write list cb.\n"); + list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list.list, list) { +diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h +index b54d9d9..da61326 100644 +--- a/drivers/misc/mei/mei_dev.h ++++ b/drivers/misc/mei/mei_dev.h +@@ -18,7 +18,7 @@ + #define _MEI_DEV_H_ + + #include +-#include ++#include + #include + #include + #include +@@ -26,33 +26,13 @@ + #include "hw.h" + #include "hbm.h" + +-/* +- * watch dog definition +- */ +-#define MEI_WD_HDR_SIZE 4 +-#define MEI_WD_STOP_MSG_SIZE MEI_WD_HDR_SIZE +-#define MEI_WD_START_MSG_SIZE (MEI_WD_HDR_SIZE + 16) +- +-#define MEI_WD_DEFAULT_TIMEOUT 120 /* seconds */ +-#define MEI_WD_MIN_TIMEOUT 120 /* seconds */ +-#define MEI_WD_MAX_TIMEOUT 65535 /* seconds */ +- +-#define MEI_WD_STOP_TIMEOUT 10 /* msecs */ +- +-#define MEI_WD_STATE_INDEPENDENCE_MSG_SENT (1 << 0) +- +-#define MEI_RD_MSG_BUF_SIZE (128 * sizeof(u32)) +- + + /* + * AMTHI Client UUID + */ + extern const uuid_le mei_amthif_guid; + +-/* +- * Watchdog Client UUID +- */ +-extern const uuid_le mei_wd_guid; ++#define MEI_RD_MSG_BUF_SIZE (128 * sizeof(u32)) + + /* + * Number of Maximum MEI Clients +@@ -78,7 +58,6 @@ extern const uuid_le mei_wd_guid; + */ + #define MEI_HOST_CLIENT_ID_ANY (-1) + #define MEI_HBM_HOST_CLIENT_ID 0 /* not used, just for documentation */ +-#define MEI_WD_HOST_CLIENT_ID 1 + #define MEI_IAMTHIF_HOST_CLIENT_ID 2 + + +@@ -123,12 +102,6 @@ enum mei_file_transaction_states { + MEI_READ_COMPLETE + }; + +-enum mei_wd_states { +- MEI_WD_IDLE, +- MEI_WD_RUNNING, +- MEI_WD_STOPPING, +-}; +- + /** + * enum mei_cb_file_ops - file operation associated with the callback + * @MEI_FOP_READ: read +@@ -404,7 +377,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); + * @wait_hw_ready : wait queue for receive HW ready message form FW + * @wait_pg : wait queue for receive PG message from FW + * @wait_hbm_start : wait queue for receive HBM start message from FW +- * @wait_stop_wd : wait queue for receive WD stop message from FW + * + * @reset_count : number of consecutive resets + * @dev_state : device state +@@ -435,12 +407,6 @@ const char *mei_pg_state_str(enum mei_pg_state state); + * + * @allow_fixed_address: allow user space to connect a fixed client + * +- * @wd_cl : watchdog client +- * @wd_state : watchdog client state +- * @wd_pending : watchdog command is pending +- * @wd_timeout : watchdog expiration timeout +- * @wd_data : watchdog message buffer +- * + * @amthif_cmd_list : amthif list for cmd waiting + * @amthif_rd_complete_list : amthif list for reading completed cmd data + * @iamthif_file_object : file for current amthif operation +@@ -486,7 +452,6 @@ struct mei_device { + wait_queue_head_t wait_hw_ready; + wait_queue_head_t wait_pg; + wait_queue_head_t wait_hbm_start; +- wait_queue_head_t wait_stop_wd; + + /* + * mei device states +@@ -531,13 +496,6 @@ struct mei_device { + + bool allow_fixed_address; + +- struct mei_cl wd_cl; +- enum mei_wd_states wd_state; +- bool wd_pending; +- u16 wd_timeout; +- unsigned char wd_data[MEI_WD_START_MSG_SIZE]; +- +- + /* amthif list for cmd waiting */ + struct mei_cl_cb amthif_cmd_list; + /* driver managed amthif list for reading completed amthif cmd data */ +@@ -649,21 +607,6 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl, + struct mei_cl_cb *complete_list); + int mei_amthif_irq_read(struct mei_device *dev, s32 *slots); + +-int mei_wd_send(struct mei_device *dev); +-int mei_wd_stop(struct mei_device *dev); +-int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl); +-/* +- * mei_watchdog_register - Registering watchdog interface +- * once we got connection to the WD Client +- * @dev: mei device +- */ +-int mei_watchdog_register(struct mei_device *dev); +-/* +- * mei_watchdog_unregister - Unregistering watchdog interface +- * @dev: mei device +- */ +-void mei_watchdog_unregister(struct mei_device *dev); +- + /* + * Register Access Function + */ +diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c +deleted file mode 100644 +index b346638..0000000 +--- a/drivers/misc/mei/wd.c ++++ /dev/null +@@ -1,391 +0,0 @@ +-/* +- * +- * Intel Management Engine Interface (Intel MEI) Linux driver +- * Copyright (c) 2003-2012, Intel Corporation. +- * +- * This program is free software; you can redistribute it and/or modify it +- * under the terms and conditions of the GNU General Public License, +- * version 2, as published by the Free Software Foundation. +- * +- * This program is distributed in the hope it will be useful, but WITHOUT +- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +- * more details. +- * +- */ +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-#include "mei_dev.h" +-#include "hbm.h" +-#include "client.h" +- +-static const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 }; +-static const u8 mei_stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 }; +- +-/* +- * AMT Watchdog Device +- */ +-#define INTEL_AMT_WATCHDOG_ID "INTCAMT" +- +-/* UUIDs for AMT F/W clients */ +-const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89, +- 0x9D, 0xA9, 0x15, 0x14, 0xCB, +- 0x32, 0xAB); +- +-static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout) +-{ +- dev_dbg(dev->dev, "wd: set timeout=%d.\n", timeout); +- memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE); +- memcpy(dev->wd_data + MEI_WD_HDR_SIZE, &timeout, sizeof(u16)); +-} +- +-/** +- * mei_wd_host_init - connect to the watchdog client +- * +- * @dev: the device structure +- * @me_cl: me client +- * +- * Return: -ENOTTY if wd client cannot be found +- * -EIO if write has failed +- * 0 on success +- */ +-int mei_wd_host_init(struct mei_device *dev, struct mei_me_client *me_cl) +-{ +- struct mei_cl *cl = &dev->wd_cl; +- int ret; +- +- mei_cl_init(cl, dev); +- +- dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT; +- dev->wd_state = MEI_WD_IDLE; +- +- ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID); +- if (ret < 0) { +- dev_info(dev->dev, "wd: failed link client\n"); +- return ret; +- } +- +- ret = mei_cl_connect(cl, me_cl, NULL); +- if (ret) { +- dev_err(dev->dev, "wd: failed to connect = %d\n", ret); +- mei_cl_unlink(cl); +- return ret; +- } +- +- ret = mei_watchdog_register(dev); +- if (ret) { +- mei_cl_disconnect(cl); +- mei_cl_unlink(cl); +- } +- return ret; +-} +- +-/** +- * mei_wd_send - sends watch dog message to fw. +- * +- * @dev: the device structure +- * +- * Return: 0 if success, +- * -EIO when message send fails +- * -EINVAL when invalid message is to be sent +- * -ENODEV on flow control failure +- */ +-int mei_wd_send(struct mei_device *dev) +-{ +- struct mei_cl *cl = &dev->wd_cl; +- struct mei_msg_hdr hdr; +- int ret; +- +- hdr.host_addr = cl->host_client_id; +- hdr.me_addr = mei_cl_me_id(cl); +- hdr.msg_complete = 1; +- hdr.reserved = 0; +- hdr.internal = 0; +- +- if (!memcmp(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE)) +- hdr.length = MEI_WD_START_MSG_SIZE; +- else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_HDR_SIZE)) +- hdr.length = MEI_WD_STOP_MSG_SIZE; +- else { +- dev_err(dev->dev, "wd: invalid message is to be sent, aborting\n"); +- return -EINVAL; +- } +- +- ret = mei_write_message(dev, &hdr, dev->wd_data); +- if (ret) { +- dev_err(dev->dev, "wd: write message failed\n"); +- return ret; +- } +- +- ret = mei_cl_flow_ctrl_reduce(cl); +- if (ret) { +- dev_err(dev->dev, "wd: flow_ctrl_reduce failed.\n"); +- return ret; +- } +- +- return 0; +-} +- +-/** +- * mei_wd_stop - sends watchdog stop message to fw. +- * +- * @dev: the device structure +- * +- * Return: 0 if success +- * on error: +- * -EIO when message send fails +- * -EINVAL when invalid message is to be sent +- * -ETIME on message timeout +- */ +-int mei_wd_stop(struct mei_device *dev) +-{ +- struct mei_cl *cl = &dev->wd_cl; +- int ret; +- +- if (!mei_cl_is_connected(cl) || +- dev->wd_state != MEI_WD_RUNNING) +- return 0; +- +- memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_STOP_MSG_SIZE); +- +- dev->wd_state = MEI_WD_STOPPING; +- +- ret = mei_cl_flow_ctrl_creds(cl); +- if (ret < 0) +- goto err; +- +- if (ret && mei_hbuf_acquire(dev)) { +- ret = mei_wd_send(dev); +- if (ret) +- goto err; +- dev->wd_pending = false; +- } else { +- dev->wd_pending = true; +- } +- +- mutex_unlock(&dev->device_lock); +- +- ret = wait_event_timeout(dev->wait_stop_wd, +- dev->wd_state == MEI_WD_IDLE, +- msecs_to_jiffies(MEI_WD_STOP_TIMEOUT)); +- mutex_lock(&dev->device_lock); +- if (dev->wd_state != MEI_WD_IDLE) { +- /* timeout */ +- ret = -ETIME; +- dev_warn(dev->dev, "wd: stop failed to complete ret=%d\n", ret); +- goto err; +- } +- dev_dbg(dev->dev, "wd: stop completed after %u msec\n", +- MEI_WD_STOP_TIMEOUT - jiffies_to_msecs(ret)); +- return 0; +-err: +- return ret; +-} +- +-/** +- * mei_wd_ops_start - wd start command from the watchdog core. +- * +- * @wd_dev: watchdog device struct +- * +- * Return: 0 if success, negative errno code for failure +- */ +-static int mei_wd_ops_start(struct watchdog_device *wd_dev) +-{ +- struct mei_device *dev; +- struct mei_cl *cl; +- int err = -ENODEV; +- +- dev = watchdog_get_drvdata(wd_dev); +- if (!dev) +- return -ENODEV; +- +- cl = &dev->wd_cl; +- +- mutex_lock(&dev->device_lock); +- +- if (dev->dev_state != MEI_DEV_ENABLED) { +- dev_dbg(dev->dev, "wd: dev_state != MEI_DEV_ENABLED dev_state = %s\n", +- mei_dev_state_str(dev->dev_state)); +- goto end_unlock; +- } +- +- if (!mei_cl_is_connected(cl)) { +- cl_dbg(dev, cl, "MEI Driver is not connected to Watchdog Client\n"); +- goto end_unlock; +- } +- +- mei_wd_set_start_timeout(dev, dev->wd_timeout); +- +- err = 0; +-end_unlock: +- mutex_unlock(&dev->device_lock); +- return err; +-} +- +-/** +- * mei_wd_ops_stop - wd stop command from the watchdog core. +- * +- * @wd_dev: watchdog device struct +- * +- * Return: 0 if success, negative errno code for failure +- */ +-static int mei_wd_ops_stop(struct watchdog_device *wd_dev) +-{ +- struct mei_device *dev; +- +- dev = watchdog_get_drvdata(wd_dev); +- if (!dev) +- return -ENODEV; +- +- mutex_lock(&dev->device_lock); +- mei_wd_stop(dev); +- mutex_unlock(&dev->device_lock); +- +- return 0; +-} +- +-/** +- * mei_wd_ops_ping - wd ping command from the watchdog core. +- * +- * @wd_dev: watchdog device struct +- * +- * Return: 0 if success, negative errno code for failure +- */ +-static int mei_wd_ops_ping(struct watchdog_device *wd_dev) +-{ +- struct mei_device *dev; +- struct mei_cl *cl; +- int ret; +- +- dev = watchdog_get_drvdata(wd_dev); +- if (!dev) +- return -ENODEV; +- +- cl = &dev->wd_cl; +- +- mutex_lock(&dev->device_lock); +- +- if (!mei_cl_is_connected(cl)) { +- cl_err(dev, cl, "wd: not connected.\n"); +- ret = -ENODEV; +- goto end; +- } +- +- dev->wd_state = MEI_WD_RUNNING; +- +- ret = mei_cl_flow_ctrl_creds(cl); +- if (ret < 0) +- goto end; +- +- /* Check if we can send the ping to HW*/ +- if (ret && mei_hbuf_acquire(dev)) { +- dev_dbg(dev->dev, "wd: sending ping\n"); +- +- ret = mei_wd_send(dev); +- if (ret) +- goto end; +- dev->wd_pending = false; +- } else { +- dev->wd_pending = true; +- } +- +-end: +- mutex_unlock(&dev->device_lock); +- return ret; +-} +- +-/** +- * mei_wd_ops_set_timeout - wd set timeout command from the watchdog core. +- * +- * @wd_dev: watchdog device struct +- * @timeout: timeout value to set +- * +- * Return: 0 if success, negative errno code for failure +- */ +-static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, +- unsigned int timeout) +-{ +- struct mei_device *dev; +- +- dev = watchdog_get_drvdata(wd_dev); +- if (!dev) +- return -ENODEV; +- +- /* Check Timeout value */ +- if (timeout < MEI_WD_MIN_TIMEOUT || timeout > MEI_WD_MAX_TIMEOUT) +- return -EINVAL; +- +- mutex_lock(&dev->device_lock); +- +- dev->wd_timeout = timeout; +- wd_dev->timeout = timeout; +- mei_wd_set_start_timeout(dev, dev->wd_timeout); +- +- mutex_unlock(&dev->device_lock); +- +- return 0; +-} +- +-/* +- * Watchdog Device structs +- */ +-static const struct watchdog_ops wd_ops = { +- .owner = THIS_MODULE, +- .start = mei_wd_ops_start, +- .stop = mei_wd_ops_stop, +- .ping = mei_wd_ops_ping, +- .set_timeout = mei_wd_ops_set_timeout, +-}; +-static const struct watchdog_info wd_info = { +- .identity = INTEL_AMT_WATCHDOG_ID, +- .options = WDIOF_KEEPALIVEPING | +- WDIOF_SETTIMEOUT | +- WDIOF_ALARMONLY, +-}; +- +-static struct watchdog_device amt_wd_dev = { +- .info = &wd_info, +- .ops = &wd_ops, +- .timeout = MEI_WD_DEFAULT_TIMEOUT, +- .min_timeout = MEI_WD_MIN_TIMEOUT, +- .max_timeout = MEI_WD_MAX_TIMEOUT, +-}; +- +- +-int mei_watchdog_register(struct mei_device *dev) +-{ +- +- int ret; +- +- amt_wd_dev.parent = dev->dev; +- /* unlock to perserve correct locking order */ +- mutex_unlock(&dev->device_lock); +- ret = watchdog_register_device(&amt_wd_dev); +- mutex_lock(&dev->device_lock); +- if (ret) { +- dev_err(dev->dev, "wd: unable to register watchdog device = %d.\n", +- ret); +- return ret; +- } +- +- dev_dbg(dev->dev, "wd: successfully register watchdog interface.\n"); +- watchdog_set_drvdata(&amt_wd_dev, dev); +- return 0; +-} +- +-void mei_watchdog_unregister(struct mei_device *dev) +-{ +- if (watchdog_get_drvdata(&amt_wd_dev) == NULL) +- return; +- +- watchdog_set_drvdata(&amt_wd_dev, NULL); +- watchdog_unregister_device(&amt_wd_dev); +-} +- +-- +cgit v0.12 + diff --git a/watchdog_implement-mei-iamt-driver.patch b/watchdog_implement-mei-iamt-driver.patch new file mode 100644 index 0000000..dc7834c --- /dev/null +++ b/watchdog_implement-mei-iamt-driver.patch @@ -0,0 +1,514 @@ +From 222818c3d84c1f3190767f5f09f2b9b9a0e0ca7f Mon Sep 17 00:00:00 2001 +From: Tomas Winkler +Date: Fri, 8 Jan 2016 00:49:22 +0200 +Subject: watchdog: mei_wdt: implement MEI iAMT watchdog driver + +Create a driver with the generic watchdog interface +for the MEI iAMT watchdog device. + +Signed-off-by: Alexander Usyskin +Signed-off-by: Tomas Winkler +Signed-off-by: Greg Kroah-Hartman + +PVE NOTES: the old code always enables this watchdog, but we do not +want to use this watchdog because it does not reboot the host, and min +timeout is 120 seconds. With this patch, we can simply blacklist mei_wdt. + +--- + Documentation/misc-devices/mei/mei.txt | 12 +- + MAINTAINERS | 1 + + drivers/watchdog/Kconfig | 15 ++ + drivers/watchdog/Makefile | 1 + + drivers/watchdog/mei_wdt.c | 404 +++++++++++++++++++++++++++++++++ + 5 files changed, 427 insertions(+), 6 deletions(-) + create mode 100644 drivers/watchdog/mei_wdt.c + +diff --git a/Documentation/misc-devices/mei/mei.txt b/Documentation/misc-devices/mei/mei.txt +index 91c1fa3..2b80a0c 100644 +--- a/Documentation/misc-devices/mei/mei.txt ++++ b/Documentation/misc-devices/mei/mei.txt +@@ -231,15 +231,15 @@ IT knows when a platform crashes even when there is a hard failure on the host. + The Intel AMT Watchdog is composed of two parts: + 1) Firmware feature - receives the heartbeats + and sends an event when the heartbeats stop. +- 2) Intel MEI driver - connects to the watchdog feature, configures the +- watchdog and sends the heartbeats. ++ 2) Intel MEI iAMT watchdog driver - connects to the watchdog feature, ++ configures the watchdog and sends the heartbeats. + +-The Intel MEI driver uses the kernel watchdog API to configure the Intel AMT +-Watchdog and to send heartbeats to it. The default timeout of the ++The Intel iAMT watchdog MEI driver uses the kernel watchdog API to configure ++the Intel AMT Watchdog and to send heartbeats to it. The default timeout of the + watchdog is 120 seconds. + +-If the Intel AMT Watchdog feature does not exist (i.e. the connection failed), +-the Intel MEI driver will disable the sending of heartbeats. ++If the Intel AMT is not enabled in the firmware then the watchdog client won't enumerate ++on the me client bus and watchdog devices won't be exposed. + + + Supported Chipsets +diff --git a/MAINTAINERS b/MAINTAINERS +index 30aca4a..d63b3c7 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -5751,6 +5751,7 @@ S: Supported + F: include/uapi/linux/mei.h + F: include/linux/mei_cl_bus.h + F: drivers/misc/mei/* ++F: drivers/watchdog/mei_wdt.c + F: Documentation/misc-devices/mei/* + + INTEL MIC DRIVERS (mic) +diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig +index 4f0e7be..57f8721 100644 +--- a/drivers/watchdog/Kconfig ++++ b/drivers/watchdog/Kconfig +@@ -1212,6 +1212,21 @@ config SBC_EPX_C3_WATCHDOG + To compile this driver as a module, choose M here: the + module will be called sbc_epx_c3. + ++config INTEL_MEI_WDT ++ tristate "Intel MEI iAMT Watchdog" ++ depends on INTEL_MEI && X86 ++ select WATCHDOG_CORE ++ ---help--- ++ A device driver for the Intel MEI iAMT watchdog. ++ ++ The Intel AMT Watchdog is an OS Health (Hang/Crash) watchdog. ++ Whenever the OS hangs or crashes, iAMT will send an event ++ to any subscriber to this event. The watchdog doesn't reset the ++ the platform. ++ ++ To compile this driver as a module, choose M here: ++ the module will be called mei_wdt. ++ + # M32R Architecture + + # M68K Architecture +diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile +index f566753..efc4f78 100644 +--- a/drivers/watchdog/Makefile ++++ b/drivers/watchdog/Makefile +@@ -126,6 +126,7 @@ obj-$(CONFIG_MACHZ_WDT) += machzwd.o + obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o + obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o + obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o ++obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o + + # M32R Architecture + +diff --git a/drivers/watchdog/mei_wdt.c b/drivers/watchdog/mei_wdt.c +new file mode 100644 +index 0000000..32e3e1d +--- /dev/null ++++ b/drivers/watchdog/mei_wdt.c +@@ -0,0 +1,404 @@ ++/* ++ * Intel Management Engine Interface (Intel MEI) Linux driver ++ * Copyright (c) 2015, Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* ++ * iAMT Watchdog Device ++ */ ++#define INTEL_AMT_WATCHDOG_ID "iamt_wdt" ++ ++#define MEI_WDT_DEFAULT_TIMEOUT 120 /* seconds */ ++#define MEI_WDT_MIN_TIMEOUT 120 /* seconds */ ++#define MEI_WDT_MAX_TIMEOUT 65535 /* seconds */ ++ ++/* Commands */ ++#define MEI_MANAGEMENT_CONTROL 0x02 ++ ++/* MEI Management Control version number */ ++#define MEI_MC_VERSION_NUMBER 0x10 ++ ++/* Sub Commands */ ++#define MEI_MC_START_WD_TIMER_REQ 0x13 ++#define MEI_MC_STOP_WD_TIMER_REQ 0x14 ++ ++/** ++ * enum mei_wdt_state - internal watchdog state ++ * ++ * @MEI_WDT_IDLE: wd is idle and not opened ++ * @MEI_WDT_START: wd was opened, start was called ++ * @MEI_WDT_RUNNING: wd is expecting keep alive pings ++ * @MEI_WDT_STOPPING: wd is stopping and will move to IDLE ++ */ ++enum mei_wdt_state { ++ MEI_WDT_IDLE, ++ MEI_WDT_START, ++ MEI_WDT_RUNNING, ++ MEI_WDT_STOPPING, ++}; ++ ++/** ++ * struct mei_wdt - mei watchdog driver ++ * @wdd: watchdog device ++ * ++ * @cldev: mei watchdog client device ++ * @state: watchdog internal state ++ * @timeout: watchdog current timeout ++ */ ++struct mei_wdt { ++ struct watchdog_device wdd; ++ ++ struct mei_cl_device *cldev; ++ enum mei_wdt_state state; ++ u16 timeout; ++}; ++ ++/* ++ * struct mei_mc_hdr - Management Control Command Header ++ * ++ * @command: Management Control (0x2) ++ * @bytecount: Number of bytes in the message beyond this byte ++ * @subcommand: Management Control Subcommand ++ * @versionnumber: Management Control Version (0x10) ++ */ ++struct mei_mc_hdr { ++ u8 command; ++ u8 bytecount; ++ u8 subcommand; ++ u8 versionnumber; ++}; ++ ++/** ++ * struct mei_wdt_start_request watchdog start/ping ++ * ++ * @hdr: Management Control Command Header ++ * @timeout: timeout value ++ * @reserved: reserved (legacy) ++ */ ++struct mei_wdt_start_request { ++ struct mei_mc_hdr hdr; ++ u16 timeout; ++ u8 reserved[17]; ++} __packed; ++ ++/** ++ * struct mei_wdt_stop_request - watchdog stop ++ * ++ * @hdr: Management Control Command Header ++ */ ++struct mei_wdt_stop_request { ++ struct mei_mc_hdr hdr; ++} __packed; ++ ++/** ++ * mei_wdt_ping - send wd start/ping command ++ * ++ * @wdt: mei watchdog device ++ * ++ * Return: 0 on success, ++ * negative errno code on failure ++ */ ++static int mei_wdt_ping(struct mei_wdt *wdt) ++{ ++ struct mei_wdt_start_request req; ++ const size_t req_len = sizeof(req); ++ int ret; ++ ++ memset(&req, 0, req_len); ++ req.hdr.command = MEI_MANAGEMENT_CONTROL; ++ req.hdr.bytecount = req_len - offsetof(struct mei_mc_hdr, subcommand); ++ req.hdr.subcommand = MEI_MC_START_WD_TIMER_REQ; ++ req.hdr.versionnumber = MEI_MC_VERSION_NUMBER; ++ req.timeout = wdt->timeout; ++ ++ ret = mei_cldev_send(wdt->cldev, (u8 *)&req, req_len); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++/** ++ * mei_wdt_stop - send wd stop command ++ * ++ * @wdt: mei watchdog device ++ * ++ * Return: 0 on success, ++ * negative errno code on failure ++ */ ++static int mei_wdt_stop(struct mei_wdt *wdt) ++{ ++ struct mei_wdt_stop_request req; ++ const size_t req_len = sizeof(req); ++ int ret; ++ ++ memset(&req, 0, req_len); ++ req.hdr.command = MEI_MANAGEMENT_CONTROL; ++ req.hdr.bytecount = req_len - offsetof(struct mei_mc_hdr, subcommand); ++ req.hdr.subcommand = MEI_MC_STOP_WD_TIMER_REQ; ++ req.hdr.versionnumber = MEI_MC_VERSION_NUMBER; ++ ++ ret = mei_cldev_send(wdt->cldev, (u8 *)&req, req_len); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++ ++/** ++ * mei_wdt_ops_start - wd start command from the watchdog core. ++ * ++ * @wdd: watchdog device ++ * ++ * Return: 0 on success or -ENODEV; ++ */ ++static int mei_wdt_ops_start(struct watchdog_device *wdd) ++{ ++ struct mei_wdt *wdt = watchdog_get_drvdata(wdd); ++ ++ wdt->state = MEI_WDT_START; ++ wdd->timeout = wdt->timeout; ++ return 0; ++} ++ ++/** ++ * mei_wdt_ops_stop - wd stop command from the watchdog core. ++ * ++ * @wdd: watchdog device ++ * ++ * Return: 0 if success, negative errno code for failure ++ */ ++static int mei_wdt_ops_stop(struct watchdog_device *wdd) ++{ ++ struct mei_wdt *wdt = watchdog_get_drvdata(wdd); ++ int ret; ++ ++ if (wdt->state != MEI_WDT_RUNNING) ++ return 0; ++ ++ wdt->state = MEI_WDT_STOPPING; ++ ++ ret = mei_wdt_stop(wdt); ++ if (ret) ++ return ret; ++ ++ wdt->state = MEI_WDT_IDLE; ++ ++ return 0; ++} ++ ++/** ++ * mei_wdt_ops_ping - wd ping command from the watchdog core. ++ * ++ * @wdd: watchdog device ++ * ++ * Return: 0 if success, negative errno code on failure ++ */ ++static int mei_wdt_ops_ping(struct watchdog_device *wdd) ++{ ++ struct mei_wdt *wdt = watchdog_get_drvdata(wdd); ++ int ret; ++ ++ if (wdt->state != MEI_WDT_START && wdt->state != MEI_WDT_RUNNING) ++ return 0; ++ ++ ret = mei_wdt_ping(wdt); ++ if (ret) ++ return ret; ++ ++ wdt->state = MEI_WDT_RUNNING; ++ ++ return 0; ++} ++ ++/** ++ * mei_wdt_ops_set_timeout - wd set timeout command from the watchdog core. ++ * ++ * @wdd: watchdog device ++ * @timeout: timeout value to set ++ * ++ * Return: 0 if success, negative errno code for failure ++ */ ++static int mei_wdt_ops_set_timeout(struct watchdog_device *wdd, ++ unsigned int timeout) ++{ ++ ++ struct mei_wdt *wdt = watchdog_get_drvdata(wdd); ++ ++ /* valid value is already checked by the caller */ ++ wdt->timeout = timeout; ++ wdd->timeout = timeout; ++ ++ return 0; ++} ++ ++static const struct watchdog_ops wd_ops = { ++ .owner = THIS_MODULE, ++ .start = mei_wdt_ops_start, ++ .stop = mei_wdt_ops_stop, ++ .ping = mei_wdt_ops_ping, ++ .set_timeout = mei_wdt_ops_set_timeout, ++}; ++ ++/* not const as the firmware_version field need to be retrieved */ ++static struct watchdog_info wd_info = { ++ .identity = INTEL_AMT_WATCHDOG_ID, ++ .options = WDIOF_KEEPALIVEPING | ++ WDIOF_SETTIMEOUT | ++ WDIOF_ALARMONLY, ++}; ++ ++/** ++ * mei_wdt_unregister - unregister from the watchdog subsystem ++ * ++ * @wdt: mei watchdog device ++ */ ++static void mei_wdt_unregister(struct mei_wdt *wdt) ++{ ++ watchdog_unregister_device(&wdt->wdd); ++ watchdog_set_drvdata(&wdt->wdd, NULL); ++} ++ ++/** ++ * mei_wdt_register - register with the watchdog subsystem ++ * ++ * @wdt: mei watchdog device ++ * ++ * Return: 0 if success, negative errno code for failure ++ */ ++static int mei_wdt_register(struct mei_wdt *wdt) ++{ ++ struct device *dev; ++ int ret; ++ ++ if (!wdt || !wdt->cldev) ++ return -EINVAL; ++ ++ dev = &wdt->cldev->dev; ++ ++ wdt->wdd.info = &wd_info; ++ wdt->wdd.ops = &wd_ops; ++ wdt->wdd.parent = dev; ++ wdt->wdd.timeout = MEI_WDT_DEFAULT_TIMEOUT; ++ wdt->wdd.min_timeout = MEI_WDT_MIN_TIMEOUT; ++ wdt->wdd.max_timeout = MEI_WDT_MAX_TIMEOUT; ++ ++ watchdog_set_drvdata(&wdt->wdd, wdt); ++ ret = watchdog_register_device(&wdt->wdd); ++ if (ret) { ++ dev_err(dev, "unable to register watchdog device = %d.\n", ret); ++ watchdog_set_drvdata(&wdt->wdd, NULL); ++ } ++ ++ return ret; ++} ++ ++static int mei_wdt_probe(struct mei_cl_device *cldev, ++ const struct mei_cl_device_id *id) ++{ ++ struct mei_wdt *wdt; ++ int ret; ++ ++ wdt = kzalloc(sizeof(struct mei_wdt), GFP_KERNEL); ++ if (!wdt) ++ return -ENOMEM; ++ ++ wdt->timeout = MEI_WDT_DEFAULT_TIMEOUT; ++ wdt->state = MEI_WDT_IDLE; ++ wdt->cldev = cldev; ++ mei_cldev_set_drvdata(cldev, wdt); ++ ++ ret = mei_cldev_enable(cldev); ++ if (ret < 0) { ++ dev_err(&cldev->dev, "Could not enable cl device\n"); ++ goto err_out; ++ } ++ ++ wd_info.firmware_version = mei_cldev_ver(cldev); ++ ++ ret = mei_wdt_register(wdt); ++ if (ret) ++ goto err_disable; ++ ++ return 0; ++ ++err_disable: ++ mei_cldev_disable(cldev); ++ ++err_out: ++ kfree(wdt); ++ ++ return ret; ++} ++ ++static int mei_wdt_remove(struct mei_cl_device *cldev) ++{ ++ struct mei_wdt *wdt = mei_cldev_get_drvdata(cldev); ++ ++ mei_wdt_unregister(wdt); ++ ++ mei_cldev_disable(cldev); ++ ++ kfree(wdt); ++ ++ return 0; ++} ++ ++#define MEI_UUID_WD UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, \ ++ 0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB) ++ ++static struct mei_cl_device_id mei_wdt_tbl[] = { ++ { .uuid = MEI_UUID_WD, .version = 0x1}, ++ /* required last entry */ ++ { } ++}; ++MODULE_DEVICE_TABLE(mei, mei_wdt_tbl); ++ ++static struct mei_cl_driver mei_wdt_driver = { ++ .id_table = mei_wdt_tbl, ++ .name = KBUILD_MODNAME, ++ ++ .probe = mei_wdt_probe, ++ .remove = mei_wdt_remove, ++}; ++ ++static int __init mei_wdt_init(void) ++{ ++ int ret; ++ ++ ret = mei_cldev_driver_register(&mei_wdt_driver); ++ if (ret) { ++ pr_err(KBUILD_MODNAME ": module registration failed\n"); ++ return ret; ++ } ++ return 0; ++} ++ ++static void __exit mei_wdt_exit(void) ++{ ++ mei_cldev_driver_unregister(&mei_wdt_driver); ++} ++ ++module_init(mei_wdt_init); ++module_exit(mei_wdt_exit); ++ ++MODULE_AUTHOR("Intel Corporation"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Device driver for Intel MEI iAMT watchdog"); +-- +cgit v0.12 + -- 2.39.2