#include "firmware_attributes_class.h"
 #include "think-lmi.h"
 
+static bool debug_support;
+module_param(debug_support, bool, 0444);
+MODULE_PARM_DESC(debug_support, "Enable debug command support");
+
 /*
  * Name:
  *  Lenovo_BiosSetting
  */
 #define LENOVO_GET_BIOS_SELECTIONS_GUID        "7364651A-132F-4FE7-ADAA-40C6C7EE2E3B"
 
+/*
+ * Name:
+ *  Lenovo_DebugCmdGUID
+ * Description
+ *  Debug entry GUID method for entering debug commands to the BIOS
+ */
+#define LENOVO_DEBUG_CMD_GUID "7FF47003-3B6C-4E5E-A227-E979824A85D1"
+
 #define TLMI_POP_PWD (1 << 0)
 #define TLMI_PAP_PWD (1 << 1)
 #define to_tlmi_pwd_setting(kobj)  container_of(kobj, struct tlmi_pwd_setting, kobj)
 
 static struct kobj_attribute pending_reboot = __ATTR_RO(pending_reboot);
 
+/* ---- Debug interface--------------------------------------------------------- */
+static ssize_t debug_cmd_store(struct kobject *kobj, struct kobj_attribute *attr,
+                               const char *buf, size_t count)
+{
+       char *set_str = NULL, *new_setting = NULL;
+       char *auth_str = NULL;
+       char *p;
+       int ret;
+
+       if (!tlmi_priv.can_debug_cmd)
+               return -EOPNOTSUPP;
+
+       new_setting = kstrdup(buf, GFP_KERNEL);
+       if (!new_setting)
+               return -ENOMEM;
+
+       /* Strip out CR if one is present */
+       p = strchrnul(new_setting, '\n');
+       *p = '\0';
+
+       if (tlmi_priv.pwd_admin->valid && tlmi_priv.pwd_admin->password[0]) {
+               auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s;",
+                               tlmi_priv.pwd_admin->password,
+                               encoding_options[tlmi_priv.pwd_admin->encoding],
+                               tlmi_priv.pwd_admin->kbdlang);
+               if (!auth_str) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+       }
+
+       if (auth_str)
+               set_str = kasprintf(GFP_KERNEL, "%s,%s", new_setting, auth_str);
+       else
+               set_str = kasprintf(GFP_KERNEL, "%s;", new_setting);
+       if (!set_str) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       ret = tlmi_simple_call(LENOVO_DEBUG_CMD_GUID, set_str);
+       if (ret)
+               goto out;
+
+       if (!ret && !tlmi_priv.pending_changes) {
+               tlmi_priv.pending_changes = true;
+               /* let userland know it may need to check reboot pending again */
+               kobject_uevent(&tlmi_priv.class_dev->kobj, KOBJ_CHANGE);
+       }
+out:
+       kfree(auth_str);
+       kfree(set_str);
+       kfree(new_setting);
+       return ret ?: count;
+}
+
+static struct kobj_attribute debug_cmd = __ATTR_WO(debug_cmd);
+
 /* ---- Initialisation --------------------------------------------------------- */
 static void tlmi_release_attr(void)
 {
                }
        }
        sysfs_remove_file(&tlmi_priv.attribute_kset->kobj, &pending_reboot.attr);
+       if (tlmi_priv.can_debug_cmd && debug_support)
+               sysfs_remove_file(&tlmi_priv.attribute_kset->kobj, &debug_cmd.attr);
        kset_unregister(tlmi_priv.attribute_kset);
 
        /* Authentication structures */
        if (ret)
                goto fail_create_attr;
 
+       if (tlmi_priv.can_debug_cmd && debug_support) {
+               ret = sysfs_create_file(&tlmi_priv.attribute_kset->kobj, &debug_cmd.attr);
+               if (ret)
+                       goto fail_create_attr;
+       }
        /* Create authentication entries */
        tlmi_priv.authentication_kset = kset_create_and_add("authentication", NULL,
                                                                &tlmi_priv.class_dev->kobj);
        if (wmi_has_guid(LENOVO_BIOS_PASSWORD_SETTINGS_GUID))
                tlmi_priv.can_get_password_settings = true;
 
+       if (wmi_has_guid(LENOVO_DEBUG_CMD_GUID))
+               tlmi_priv.can_debug_cmd = true;
+
        /*
         * Try to find the number of valid settings of this machine
         * and use it to create sysfs attributes.