.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright 2010 Google Inc. All Rights Reserved. |
---|
3 | 4 | * Author: dlaurie@google.com (Duncan Laurie) |
---|
.. | .. |
---|
29 | 30 | #include <linux/efi.h> |
---|
30 | 31 | #include <linux/module.h> |
---|
31 | 32 | #include <linux/ucs2_string.h> |
---|
| 33 | +#include <linux/suspend.h> |
---|
32 | 34 | |
---|
33 | 35 | #define GSMI_SHUTDOWN_CLEAN 0 /* Clean Shutdown */ |
---|
34 | 36 | /* TODO(mikew@google.com): Tie in HARDLOCKUP_DETECTOR with NMIWDT */ |
---|
.. | .. |
---|
70 | 72 | #define GSMI_CMD_SET_NVRAM_VAR 0x03 |
---|
71 | 73 | #define GSMI_CMD_SET_EVENT_LOG 0x08 |
---|
72 | 74 | #define GSMI_CMD_CLEAR_EVENT_LOG 0x09 |
---|
| 75 | +#define GSMI_CMD_LOG_S0IX_SUSPEND 0x0a |
---|
| 76 | +#define GSMI_CMD_LOG_S0IX_RESUME 0x0b |
---|
73 | 77 | #define GSMI_CMD_CLEAR_CONFIG 0x20 |
---|
74 | 78 | #define GSMI_CMD_HANDSHAKE_TYPE 0xC1 |
---|
| 79 | +#define GSMI_CMD_RESERVED 0xff |
---|
75 | 80 | |
---|
76 | 81 | /* Magic entry type for kernel events */ |
---|
77 | 82 | #define GSMI_LOG_ENTRY_TYPE_KERNEL 0xDEAD |
---|
.. | .. |
---|
84 | 89 | u32 address; /* physical address of buffer */ |
---|
85 | 90 | }; |
---|
86 | 91 | |
---|
87 | | -struct gsmi_device { |
---|
| 92 | +static struct gsmi_device { |
---|
88 | 93 | struct platform_device *pdev; /* platform device */ |
---|
89 | 94 | struct gsmi_buf *name_buf; /* variable name buffer */ |
---|
90 | 95 | struct gsmi_buf *data_buf; /* generic data buffer */ |
---|
.. | .. |
---|
122 | 127 | u32 instance; |
---|
123 | 128 | } __packed; |
---|
124 | 129 | |
---|
125 | | - |
---|
126 | 130 | /* |
---|
127 | 131 | * Some platforms don't have explicit SMI handshake |
---|
128 | 132 | * and need to wait for SMI to complete. |
---|
.. | .. |
---|
132 | 136 | module_param(spincount, uint, 0600); |
---|
133 | 137 | MODULE_PARM_DESC(spincount, |
---|
134 | 138 | "The number of loop iterations to use when using the spin handshake."); |
---|
| 139 | + |
---|
| 140 | +/* |
---|
| 141 | + * Platforms might not support S0ix logging in their GSMI handlers. In order to |
---|
| 142 | + * avoid any side-effects of generating an SMI for S0ix logging, use the S0ix |
---|
| 143 | + * related GSMI commands only for those platforms that explicitly enable this |
---|
| 144 | + * option. |
---|
| 145 | + */ |
---|
| 146 | +static bool s0ix_logging_enable; |
---|
| 147 | +module_param(s0ix_logging_enable, bool, 0600); |
---|
135 | 148 | |
---|
136 | 149 | static struct gsmi_buf *gsmi_buf_alloc(void) |
---|
137 | 150 | { |
---|
.. | .. |
---|
289 | 302 | return rc; |
---|
290 | 303 | } |
---|
291 | 304 | |
---|
| 305 | +#ifdef CONFIG_EFI |
---|
| 306 | + |
---|
| 307 | +static struct efivars efivars; |
---|
| 308 | + |
---|
292 | 309 | static efi_status_t gsmi_get_variable(efi_char16_t *name, |
---|
293 | 310 | efi_guid_t *vendor, u32 *attr, |
---|
294 | 311 | unsigned long *data_size, |
---|
.. | .. |
---|
343 | 360 | memcpy(data, gsmi_dev.data_buf->start, *data_size); |
---|
344 | 361 | |
---|
345 | 362 | /* All variables are have the following attributes */ |
---|
346 | | - *attr = EFI_VARIABLE_NON_VOLATILE | |
---|
347 | | - EFI_VARIABLE_BOOTSERVICE_ACCESS | |
---|
348 | | - EFI_VARIABLE_RUNTIME_ACCESS; |
---|
| 363 | + if (attr) |
---|
| 364 | + *attr = EFI_VARIABLE_NON_VOLATILE | |
---|
| 365 | + EFI_VARIABLE_BOOTSERVICE_ACCESS | |
---|
| 366 | + EFI_VARIABLE_RUNTIME_ACCESS; |
---|
349 | 367 | } |
---|
350 | 368 | |
---|
351 | 369 | spin_unlock_irqrestore(&gsmi_dev.lock, flags); |
---|
.. | .. |
---|
465 | 483 | .set_variable = gsmi_set_variable, |
---|
466 | 484 | .get_next_variable = gsmi_get_next_variable, |
---|
467 | 485 | }; |
---|
| 486 | + |
---|
| 487 | +#endif /* CONFIG_EFI */ |
---|
468 | 488 | |
---|
469 | 489 | static ssize_t eventlog_write(struct file *filp, struct kobject *kobj, |
---|
470 | 490 | struct bin_attribute *bin_attr, |
---|
.. | .. |
---|
661 | 681 | static int gsmi_panic_callback(struct notifier_block *nb, |
---|
662 | 682 | unsigned long reason, void *arg) |
---|
663 | 683 | { |
---|
| 684 | + |
---|
| 685 | + /* |
---|
| 686 | + * Panic callbacks are executed with all other CPUs stopped, |
---|
| 687 | + * so we must not attempt to spin waiting for gsmi_dev.lock |
---|
| 688 | + * to be released. |
---|
| 689 | + */ |
---|
| 690 | + if (spin_is_locked(&gsmi_dev.lock)) |
---|
| 691 | + return NOTIFY_DONE; |
---|
| 692 | + |
---|
664 | 693 | gsmi_shutdown_reason(GSMI_SHUTDOWN_PANIC); |
---|
665 | 694 | return NOTIFY_DONE; |
---|
666 | 695 | } |
---|
.. | .. |
---|
715 | 744 | DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."), |
---|
716 | 745 | }, |
---|
717 | 746 | }, |
---|
| 747 | + { |
---|
| 748 | + .ident = "Coreboot Firmware", |
---|
| 749 | + .matches = { |
---|
| 750 | + DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), |
---|
| 751 | + }, |
---|
| 752 | + }, |
---|
718 | 753 | {} |
---|
719 | 754 | }; |
---|
720 | 755 | MODULE_DEVICE_TABLE(dmi, gsmi_dmi_table); |
---|
.. | .. |
---|
722 | 757 | static __init int gsmi_system_valid(void) |
---|
723 | 758 | { |
---|
724 | 759 | u32 hash; |
---|
| 760 | + u16 cmd, result; |
---|
725 | 761 | |
---|
726 | 762 | if (!dmi_check_system(gsmi_dmi_table)) |
---|
727 | 763 | return -ENODEV; |
---|
.. | .. |
---|
756 | 792 | return -ENODEV; |
---|
757 | 793 | } |
---|
758 | 794 | |
---|
| 795 | + /* Test the smihandler with a bogus command. If it leaves the |
---|
| 796 | + * calling argument in %ax untouched, there is no handler for |
---|
| 797 | + * GSMI commands. |
---|
| 798 | + */ |
---|
| 799 | + cmd = GSMI_CALLBACK | GSMI_CMD_RESERVED << 8; |
---|
| 800 | + asm volatile ( |
---|
| 801 | + "outb %%al, %%dx\n\t" |
---|
| 802 | + : "=a" (result) |
---|
| 803 | + : "0" (cmd), |
---|
| 804 | + "d" (acpi_gbl_FADT.smi_command) |
---|
| 805 | + : "memory", "cc" |
---|
| 806 | + ); |
---|
| 807 | + if (cmd == result) { |
---|
| 808 | + pr_info("gsmi: no gsmi handler in firmware\n"); |
---|
| 809 | + return -ENODEV; |
---|
| 810 | + } |
---|
| 811 | + |
---|
759 | 812 | /* Found */ |
---|
760 | 813 | return 0; |
---|
761 | 814 | } |
---|
762 | 815 | |
---|
763 | 816 | static struct kobject *gsmi_kobj; |
---|
764 | | -static struct efivars efivars; |
---|
765 | 817 | |
---|
766 | 818 | static const struct platform_device_info gsmi_dev_info = { |
---|
767 | 819 | .name = "gsmi", |
---|
.. | .. |
---|
769 | 821 | /* SMI callbacks require 32bit addresses */ |
---|
770 | 822 | .dma_mask = DMA_BIT_MASK(32), |
---|
771 | 823 | }; |
---|
| 824 | + |
---|
| 825 | +#ifdef CONFIG_PM |
---|
| 826 | +static void gsmi_log_s0ix_info(u8 cmd) |
---|
| 827 | +{ |
---|
| 828 | + unsigned long flags; |
---|
| 829 | + |
---|
| 830 | + /* |
---|
| 831 | + * If platform has not enabled S0ix logging, then no action is |
---|
| 832 | + * necessary. |
---|
| 833 | + */ |
---|
| 834 | + if (!s0ix_logging_enable) |
---|
| 835 | + return; |
---|
| 836 | + |
---|
| 837 | + spin_lock_irqsave(&gsmi_dev.lock, flags); |
---|
| 838 | + |
---|
| 839 | + memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length); |
---|
| 840 | + |
---|
| 841 | + gsmi_exec(GSMI_CALLBACK, cmd); |
---|
| 842 | + |
---|
| 843 | + spin_unlock_irqrestore(&gsmi_dev.lock, flags); |
---|
| 844 | +} |
---|
| 845 | + |
---|
| 846 | +static int gsmi_log_s0ix_suspend(struct device *dev) |
---|
| 847 | +{ |
---|
| 848 | + /* |
---|
| 849 | + * If system is not suspending via firmware using the standard ACPI Sx |
---|
| 850 | + * types, then make a GSMI call to log the suspend info. |
---|
| 851 | + */ |
---|
| 852 | + if (!pm_suspend_via_firmware()) |
---|
| 853 | + gsmi_log_s0ix_info(GSMI_CMD_LOG_S0IX_SUSPEND); |
---|
| 854 | + |
---|
| 855 | + /* |
---|
| 856 | + * Always return success, since we do not want suspend |
---|
| 857 | + * to fail just because of logging failure. |
---|
| 858 | + */ |
---|
| 859 | + return 0; |
---|
| 860 | +} |
---|
| 861 | + |
---|
| 862 | +static int gsmi_log_s0ix_resume(struct device *dev) |
---|
| 863 | +{ |
---|
| 864 | + /* |
---|
| 865 | + * If system did not resume via firmware, then make a GSMI call to log |
---|
| 866 | + * the resume info and wake source. |
---|
| 867 | + */ |
---|
| 868 | + if (!pm_resume_via_firmware()) |
---|
| 869 | + gsmi_log_s0ix_info(GSMI_CMD_LOG_S0IX_RESUME); |
---|
| 870 | + |
---|
| 871 | + /* |
---|
| 872 | + * Always return success, since we do not want resume |
---|
| 873 | + * to fail just because of logging failure. |
---|
| 874 | + */ |
---|
| 875 | + return 0; |
---|
| 876 | +} |
---|
| 877 | + |
---|
| 878 | +static const struct dev_pm_ops gsmi_pm_ops = { |
---|
| 879 | + .suspend_noirq = gsmi_log_s0ix_suspend, |
---|
| 880 | + .resume_noirq = gsmi_log_s0ix_resume, |
---|
| 881 | +}; |
---|
| 882 | + |
---|
| 883 | +static int gsmi_platform_driver_probe(struct platform_device *dev) |
---|
| 884 | +{ |
---|
| 885 | + return 0; |
---|
| 886 | +} |
---|
| 887 | + |
---|
| 888 | +static struct platform_driver gsmi_driver_info = { |
---|
| 889 | + .driver = { |
---|
| 890 | + .name = "gsmi", |
---|
| 891 | + .pm = &gsmi_pm_ops, |
---|
| 892 | + }, |
---|
| 893 | + .probe = gsmi_platform_driver_probe, |
---|
| 894 | +}; |
---|
| 895 | +#endif |
---|
772 | 896 | |
---|
773 | 897 | static __init int gsmi_init(void) |
---|
774 | 898 | { |
---|
.. | .. |
---|
780 | 904 | return ret; |
---|
781 | 905 | |
---|
782 | 906 | gsmi_dev.smi_cmd = acpi_gbl_FADT.smi_command; |
---|
| 907 | + |
---|
| 908 | +#ifdef CONFIG_PM |
---|
| 909 | + ret = platform_driver_register(&gsmi_driver_info); |
---|
| 910 | + if (unlikely(ret)) { |
---|
| 911 | + printk(KERN_ERR "gsmi: unable to register platform driver\n"); |
---|
| 912 | + return ret; |
---|
| 913 | + } |
---|
| 914 | +#endif |
---|
783 | 915 | |
---|
784 | 916 | /* register device */ |
---|
785 | 917 | gsmi_dev.pdev = platform_device_register_full(&gsmi_dev_info); |
---|
.. | .. |
---|
885 | 1017 | goto out_remove_bin_file; |
---|
886 | 1018 | } |
---|
887 | 1019 | |
---|
| 1020 | +#ifdef CONFIG_EFI |
---|
888 | 1021 | ret = efivars_register(&efivars, &efivar_ops, gsmi_kobj); |
---|
889 | 1022 | if (ret) { |
---|
890 | 1023 | printk(KERN_INFO "gsmi: Failed to register efivars\n"); |
---|
891 | | - goto out_remove_sysfs_files; |
---|
| 1024 | + sysfs_remove_files(gsmi_kobj, gsmi_attrs); |
---|
| 1025 | + goto out_remove_bin_file; |
---|
892 | 1026 | } |
---|
| 1027 | +#endif |
---|
893 | 1028 | |
---|
894 | 1029 | register_reboot_notifier(&gsmi_reboot_notifier); |
---|
895 | 1030 | register_die_notifier(&gsmi_die_notifier); |
---|
.. | .. |
---|
900 | 1035 | |
---|
901 | 1036 | return 0; |
---|
902 | 1037 | |
---|
903 | | -out_remove_sysfs_files: |
---|
904 | | - sysfs_remove_files(gsmi_kobj, gsmi_attrs); |
---|
905 | 1038 | out_remove_bin_file: |
---|
906 | 1039 | sysfs_remove_bin_file(gsmi_kobj, &eventlog_bin_attr); |
---|
907 | 1040 | out_err: |
---|
.. | .. |
---|
912 | 1045 | dma_pool_destroy(gsmi_dev.dma_pool); |
---|
913 | 1046 | platform_device_unregister(gsmi_dev.pdev); |
---|
914 | 1047 | pr_info("gsmi: failed to load: %d\n", ret); |
---|
| 1048 | +#ifdef CONFIG_PM |
---|
| 1049 | + platform_driver_unregister(&gsmi_driver_info); |
---|
| 1050 | +#endif |
---|
915 | 1051 | return ret; |
---|
916 | 1052 | } |
---|
917 | 1053 | |
---|
.. | .. |
---|
921 | 1057 | unregister_die_notifier(&gsmi_die_notifier); |
---|
922 | 1058 | atomic_notifier_chain_unregister(&panic_notifier_list, |
---|
923 | 1059 | &gsmi_panic_notifier); |
---|
| 1060 | +#ifdef CONFIG_EFI |
---|
924 | 1061 | efivars_unregister(&efivars); |
---|
| 1062 | +#endif |
---|
925 | 1063 | |
---|
926 | 1064 | sysfs_remove_files(gsmi_kobj, gsmi_attrs); |
---|
927 | 1065 | sysfs_remove_bin_file(gsmi_kobj, &eventlog_bin_attr); |
---|
.. | .. |
---|
931 | 1069 | gsmi_buf_free(gsmi_dev.name_buf); |
---|
932 | 1070 | dma_pool_destroy(gsmi_dev.dma_pool); |
---|
933 | 1071 | platform_device_unregister(gsmi_dev.pdev); |
---|
| 1072 | +#ifdef CONFIG_PM |
---|
| 1073 | + platform_driver_unregister(&gsmi_driver_info); |
---|
| 1074 | +#endif |
---|
934 | 1075 | } |
---|
935 | 1076 | |
---|
936 | 1077 | module_init(gsmi_init); |
---|