aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorHenrique de Moraes Holschuh <hmh@hmh.eng.br>2009-04-04 00:25:49 -0400
committerLen Brown <len.brown@intel.com>2009-04-04 03:14:52 -0400
commita4d5effcc73749ee3ebbf578d162905e6fa4e07d (patch)
tree1160b3763004be227cfe3d6c15e4235a9ccf69b7 /drivers/platform
parent2586d5663d0a17d69383acf6110f16a979a07c4e (diff)
thinkpad-acpi: restrict access to some firmware LEDs
Some of the ThinkPad LEDs indicate critical conditions that can cause data loss or cause hardware damage when ignored (e.g. force-ejecting a powered up bay; ignoring a failing battery, or empty battery; force- undocking with the dock buses still active, etc). On almost all ThinkPads, LED access is write-only, and the firmware usually does fire-and-forget signaling on them, so you effectively lose whatever message the firmware was trying to convey to the user when you override the LED state, without any chance to restore it. Restrict access to all LEDs that can convey important alarms, or that could mislead the user into incorrectly operating the hardware. This will make the Lenovo engineers less unhappy about the whole issue. Allow users that really want it to still control all LEDs, it is the unaware user that we have to worry about. Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/Kconfig24
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c76
2 files changed, 80 insertions, 20 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 3608081bc3e0..d45c6ab729f8 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -226,6 +226,30 @@ config THINKPAD_ACPI_DEBUG
226 226
227 If you are not sure, say N here. 227 If you are not sure, say N here.
228 228
229config THINKPAD_ACPI_UNSAFE_LEDS
230 bool "Allow control of important LEDs (unsafe)"
231 depends on THINKPAD_ACPI
232 default n
233 ---help---
234 Overriding LED state on ThinkPads can mask important
235 firmware alerts (like critical battery condition), or misled
236 the user into damaging the hardware (undocking or ejecting
237 the bay while buses are still active), etc.
238
239 LED control on the ThinkPad is write-only (with very few
240 exceptions on very ancient models), which makes it
241 impossible to know beforehand if important information will
242 be lost when one changes LED state.
243
244 Users that know what they are doing can enable this option
245 and the driver will allow control of every LED, including
246 the ones on the dock stations.
247
248 Never enable this option on a distribution kernel.
249
250 Say N here, unless you are building a kernel for your own
251 use, and need to control the important firmware LEDs.
252
229config THINKPAD_ACPI_DOCK 253config THINKPAD_ACPI_DOCK
230 bool "Legacy Docking Station Support" 254 bool "Legacy Docking Station Support"
231 depends on THINKPAD_ACPI 255 depends on THINKPAD_ACPI
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index f003fb7c79ca..38c34c79ff35 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -4657,6 +4657,16 @@ static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = {
4657 "tpacpi::unknown_led", 4657 "tpacpi::unknown_led",
4658 "tpacpi::standby", 4658 "tpacpi::standby",
4659}; 4659};
4660#define TPACPI_SAFE_LEDS 0x0081U
4661
4662static inline bool tpacpi_is_led_restricted(const unsigned int led)
4663{
4664#ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS
4665 return false;
4666#else
4667 return (TPACPI_SAFE_LEDS & (1 << led)) == 0;
4668#endif
4669}
4660 4670
4661static int led_get_status(const unsigned int led) 4671static int led_get_status(const unsigned int led)
4662{ 4672{
@@ -4694,16 +4704,20 @@ static int led_set_status(const unsigned int led,
4694 switch (led_supported) { 4704 switch (led_supported) {
4695 case TPACPI_LED_570: 4705 case TPACPI_LED_570:
4696 /* 570 */ 4706 /* 570 */
4697 if (led > 7) 4707 if (unlikely(led > 7))
4698 return -EINVAL; 4708 return -EINVAL;
4709 if (unlikely(tpacpi_is_led_restricted(led)))
4710 return -EPERM;
4699 if (!acpi_evalf(led_handle, NULL, NULL, "vdd", 4711 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
4700 (1 << led), led_sled_arg1[ledstatus])) 4712 (1 << led), led_sled_arg1[ledstatus]))
4701 rc = -EIO; 4713 rc = -EIO;
4702 break; 4714 break;
4703 case TPACPI_LED_OLD: 4715 case TPACPI_LED_OLD:
4704 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ 4716 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
4705 if (led > 7) 4717 if (unlikely(led > 7))
4706 return -EINVAL; 4718 return -EINVAL;
4719 if (unlikely(tpacpi_is_led_restricted(led)))
4720 return -EPERM;
4707 rc = ec_write(TPACPI_LED_EC_HLMS, (1 << led)); 4721 rc = ec_write(TPACPI_LED_EC_HLMS, (1 << led));
4708 if (rc >= 0) 4722 if (rc >= 0)
4709 rc = ec_write(TPACPI_LED_EC_HLBL, 4723 rc = ec_write(TPACPI_LED_EC_HLBL,
@@ -4714,6 +4728,10 @@ static int led_set_status(const unsigned int led,
4714 break; 4728 break;
4715 case TPACPI_LED_NEW: 4729 case TPACPI_LED_NEW:
4716 /* all others */ 4730 /* all others */
4731 if (unlikely(led >= TPACPI_LED_NUMLEDS))
4732 return -EINVAL;
4733 if (unlikely(tpacpi_is_led_restricted(led)))
4734 return -EPERM;
4717 if (!acpi_evalf(led_handle, NULL, NULL, "vdd", 4735 if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
4718 led, led_led_arg1[ledstatus])) 4736 led, led_led_arg1[ledstatus]))
4719 rc = -EIO; 4737 rc = -EIO;
@@ -4806,6 +4824,30 @@ static void led_exit(void)
4806 kfree(tpacpi_leds); 4824 kfree(tpacpi_leds);
4807} 4825}
4808 4826
4827static int __init tpacpi_init_led(unsigned int led)
4828{
4829 int rc;
4830
4831 tpacpi_leds[led].led = led;
4832
4833 tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set;
4834 tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set;
4835 if (led_supported == TPACPI_LED_570)
4836 tpacpi_leds[led].led_classdev.brightness_get =
4837 &led_sysfs_get;
4838
4839 tpacpi_leds[led].led_classdev.name = tpacpi_led_names[led];
4840
4841 INIT_WORK(&tpacpi_leds[led].work, led_set_status_worker);
4842
4843 rc = led_classdev_register(&tpacpi_pdev->dev,
4844 &tpacpi_leds[led].led_classdev);
4845 if (rc < 0)
4846 tpacpi_leds[led].led_classdev.name = NULL;
4847
4848 return rc;
4849}
4850
4809static int __init led_init(struct ibm_init_struct *iibm) 4851static int __init led_init(struct ibm_init_struct *iibm)
4810{ 4852{
4811 unsigned int i; 4853 unsigned int i;
@@ -4839,27 +4881,21 @@ static int __init led_init(struct ibm_init_struct *iibm)
4839 } 4881 }
4840 4882
4841 for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { 4883 for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
4842 tpacpi_leds[i].led = i; 4884 if (!tpacpi_is_led_restricted(i)) {
4843 4885 rc = tpacpi_init_led(i);
4844 tpacpi_leds[i].led_classdev.brightness_set = &led_sysfs_set; 4886 if (rc < 0) {
4845 tpacpi_leds[i].led_classdev.blink_set = &led_sysfs_blink_set; 4887 led_exit();
4846 if (led_supported == TPACPI_LED_570) 4888 return rc;
4847 tpacpi_leds[i].led_classdev.brightness_get = 4889 }
4848 &led_sysfs_get;
4849
4850 tpacpi_leds[i].led_classdev.name = tpacpi_led_names[i];
4851
4852 INIT_WORK(&tpacpi_leds[i].work, led_set_status_worker);
4853
4854 rc = led_classdev_register(&tpacpi_pdev->dev,
4855 &tpacpi_leds[i].led_classdev);
4856 if (rc < 0) {
4857 tpacpi_leds[i].led_classdev.name = NULL;
4858 led_exit();
4859 return rc;
4860 } 4890 }
4861 } 4891 }
4862 4892
4893#ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS
4894 if (led_supported != TPACPI_LED_NONE)
4895 printk(TPACPI_NOTICE
4896 "warning: userspace override of important "
4897 "firmware LEDs is enabled\n");
4898#endif
4863 return (led_supported != TPACPI_LED_NONE)? 0 : 1; 4899 return (led_supported != TPACPI_LED_NONE)? 0 : 1;
4864} 4900}
4865 4901