.. | .. |
---|
| 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 | { |
---|
.. | .. |
---|
288 | 301 | |
---|
289 | 302 | return rc; |
---|
290 | 303 | } |
---|
| 304 | + |
---|
| 305 | +#ifdef CONFIG_EFI |
---|
| 306 | + |
---|
| 307 | +static struct efivars efivars; |
---|
291 | 308 | |
---|
292 | 309 | static efi_status_t gsmi_get_variable(efi_char16_t *name, |
---|
293 | 310 | efi_guid_t *vendor, u32 *attr, |
---|
.. | .. |
---|
465 | 482 | .set_variable = gsmi_set_variable, |
---|
466 | 483 | .get_next_variable = gsmi_get_next_variable, |
---|
467 | 484 | }; |
---|
| 485 | + |
---|
| 486 | +#endif /* CONFIG_EFI */ |
---|
468 | 487 | |
---|
469 | 488 | static ssize_t eventlog_write(struct file *filp, struct kobject *kobj, |
---|
470 | 489 | struct bin_attribute *bin_attr, |
---|
.. | .. |
---|
661 | 680 | static int gsmi_panic_callback(struct notifier_block *nb, |
---|
662 | 681 | unsigned long reason, void *arg) |
---|
663 | 682 | { |
---|
| 683 | + |
---|
| 684 | + /* |
---|
| 685 | + * Panic callbacks are executed with all other CPUs stopped, |
---|
| 686 | + * so we must not attempt to spin waiting for gsmi_dev.lock |
---|
| 687 | + * to be released. |
---|
| 688 | + */ |
---|
| 689 | + if (spin_is_locked(&gsmi_dev.lock)) |
---|
| 690 | + return NOTIFY_DONE; |
---|
| 691 | + |
---|
664 | 692 | gsmi_shutdown_reason(GSMI_SHUTDOWN_PANIC); |
---|
665 | 693 | return NOTIFY_DONE; |
---|
666 | 694 | } |
---|
.. | .. |
---|
715 | 743 | DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."), |
---|
716 | 744 | }, |
---|
717 | 745 | }, |
---|
| 746 | + { |
---|
| 747 | + .ident = "Coreboot Firmware", |
---|
| 748 | + .matches = { |
---|
| 749 | + DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), |
---|
| 750 | + }, |
---|
| 751 | + }, |
---|
718 | 752 | {} |
---|
719 | 753 | }; |
---|
720 | 754 | MODULE_DEVICE_TABLE(dmi, gsmi_dmi_table); |
---|
.. | .. |
---|
722 | 756 | static __init int gsmi_system_valid(void) |
---|
723 | 757 | { |
---|
724 | 758 | u32 hash; |
---|
| 759 | + u16 cmd, result; |
---|
725 | 760 | |
---|
726 | 761 | if (!dmi_check_system(gsmi_dmi_table)) |
---|
727 | 762 | return -ENODEV; |
---|
.. | .. |
---|
756 | 791 | return -ENODEV; |
---|
757 | 792 | } |
---|
758 | 793 | |
---|
| 794 | + /* Test the smihandler with a bogus command. If it leaves the |
---|
| 795 | + * calling argument in %ax untouched, there is no handler for |
---|
| 796 | + * GSMI commands. |
---|
| 797 | + */ |
---|
| 798 | + cmd = GSMI_CALLBACK | GSMI_CMD_RESERVED << 8; |
---|
| 799 | + asm volatile ( |
---|
| 800 | + "outb %%al, %%dx\n\t" |
---|
| 801 | + : "=a" (result) |
---|
| 802 | + : "0" (cmd), |
---|
| 803 | + "d" (acpi_gbl_FADT.smi_command) |
---|
| 804 | + : "memory", "cc" |
---|
| 805 | + ); |
---|
| 806 | + if (cmd == result) { |
---|
| 807 | + pr_info("gsmi: no gsmi handler in firmware\n"); |
---|
| 808 | + return -ENODEV; |
---|
| 809 | + } |
---|
| 810 | + |
---|
759 | 811 | /* Found */ |
---|
760 | 812 | return 0; |
---|
761 | 813 | } |
---|
762 | 814 | |
---|
763 | 815 | static struct kobject *gsmi_kobj; |
---|
764 | | -static struct efivars efivars; |
---|
765 | 816 | |
---|
766 | 817 | static const struct platform_device_info gsmi_dev_info = { |
---|
767 | 818 | .name = "gsmi", |
---|
.. | .. |
---|
769 | 820 | /* SMI callbacks require 32bit addresses */ |
---|
770 | 821 | .dma_mask = DMA_BIT_MASK(32), |
---|
771 | 822 | }; |
---|
| 823 | + |
---|
| 824 | +#ifdef CONFIG_PM |
---|
| 825 | +static void gsmi_log_s0ix_info(u8 cmd) |
---|
| 826 | +{ |
---|
| 827 | + unsigned long flags; |
---|
| 828 | + |
---|
| 829 | + /* |
---|
| 830 | + * If platform has not enabled S0ix logging, then no action is |
---|
| 831 | + * necessary. |
---|
| 832 | + */ |
---|
| 833 | + if (!s0ix_logging_enable) |
---|
| 834 | + return; |
---|
| 835 | + |
---|
| 836 | + spin_lock_irqsave(&gsmi_dev.lock, flags); |
---|
| 837 | + |
---|
| 838 | + memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length); |
---|
| 839 | + |
---|
| 840 | + gsmi_exec(GSMI_CALLBACK, cmd); |
---|
| 841 | + |
---|
| 842 | + spin_unlock_irqrestore(&gsmi_dev.lock, flags); |
---|
| 843 | +} |
---|
| 844 | + |
---|
| 845 | +static int gsmi_log_s0ix_suspend(struct device *dev) |
---|
| 846 | +{ |
---|
| 847 | + /* |
---|
| 848 | + * If system is not suspending via firmware using the standard ACPI Sx |
---|
| 849 | + * types, then make a GSMI call to log the suspend info. |
---|
| 850 | + */ |
---|
| 851 | + if (!pm_suspend_via_firmware()) |
---|
| 852 | + gsmi_log_s0ix_info(GSMI_CMD_LOG_S0IX_SUSPEND); |
---|
| 853 | + |
---|
| 854 | + /* |
---|
| 855 | + * Always return success, since we do not want suspend |
---|
| 856 | + * to fail just because of logging failure. |
---|
| 857 | + */ |
---|
| 858 | + return 0; |
---|
| 859 | +} |
---|
| 860 | + |
---|
| 861 | +static int gsmi_log_s0ix_resume(struct device *dev) |
---|
| 862 | +{ |
---|
| 863 | + /* |
---|
| 864 | + * If system did not resume via firmware, then make a GSMI call to log |
---|
| 865 | + * the resume info and wake source. |
---|
| 866 | + */ |
---|
| 867 | + if (!pm_resume_via_firmware()) |
---|
| 868 | + gsmi_log_s0ix_info(GSMI_CMD_LOG_S0IX_RESUME); |
---|
| 869 | + |
---|
| 870 | + /* |
---|
| 871 | + * Always return success, since we do not want resume |
---|
| 872 | + * to fail just because of logging failure. |
---|
| 873 | + */ |
---|
| 874 | + return 0; |
---|
| 875 | +} |
---|
| 876 | + |
---|
| 877 | +static const struct dev_pm_ops gsmi_pm_ops = { |
---|
| 878 | + .suspend_noirq = gsmi_log_s0ix_suspend, |
---|
| 879 | + .resume_noirq = gsmi_log_s0ix_resume, |
---|
| 880 | +}; |
---|
| 881 | + |
---|
| 882 | +static int gsmi_platform_driver_probe(struct platform_device *dev) |
---|
| 883 | +{ |
---|
| 884 | + return 0; |
---|
| 885 | +} |
---|
| 886 | + |
---|
| 887 | +static struct platform_driver gsmi_driver_info = { |
---|
| 888 | + .driver = { |
---|
| 889 | + .name = "gsmi", |
---|
| 890 | + .pm = &gsmi_pm_ops, |
---|
| 891 | + }, |
---|
| 892 | + .probe = gsmi_platform_driver_probe, |
---|
| 893 | +}; |
---|
| 894 | +#endif |
---|
772 | 895 | |
---|
773 | 896 | static __init int gsmi_init(void) |
---|
774 | 897 | { |
---|
.. | .. |
---|
780 | 903 | return ret; |
---|
781 | 904 | |
---|
782 | 905 | gsmi_dev.smi_cmd = acpi_gbl_FADT.smi_command; |
---|
| 906 | + |
---|
| 907 | +#ifdef CONFIG_PM |
---|
| 908 | + ret = platform_driver_register(&gsmi_driver_info); |
---|
| 909 | + if (unlikely(ret)) { |
---|
| 910 | + printk(KERN_ERR "gsmi: unable to register platform driver\n"); |
---|
| 911 | + return ret; |
---|
| 912 | + } |
---|
| 913 | +#endif |
---|
783 | 914 | |
---|
784 | 915 | /* register device */ |
---|
785 | 916 | gsmi_dev.pdev = platform_device_register_full(&gsmi_dev_info); |
---|
.. | .. |
---|
885 | 1016 | goto out_remove_bin_file; |
---|
886 | 1017 | } |
---|
887 | 1018 | |
---|
| 1019 | +#ifdef CONFIG_EFI |
---|
888 | 1020 | ret = efivars_register(&efivars, &efivar_ops, gsmi_kobj); |
---|
889 | 1021 | if (ret) { |
---|
890 | 1022 | printk(KERN_INFO "gsmi: Failed to register efivars\n"); |
---|
891 | | - goto out_remove_sysfs_files; |
---|
| 1023 | + sysfs_remove_files(gsmi_kobj, gsmi_attrs); |
---|
| 1024 | + goto out_remove_bin_file; |
---|
892 | 1025 | } |
---|
| 1026 | +#endif |
---|
893 | 1027 | |
---|
894 | 1028 | register_reboot_notifier(&gsmi_reboot_notifier); |
---|
895 | 1029 | register_die_notifier(&gsmi_die_notifier); |
---|
.. | .. |
---|
900 | 1034 | |
---|
901 | 1035 | return 0; |
---|
902 | 1036 | |
---|
903 | | -out_remove_sysfs_files: |
---|
904 | | - sysfs_remove_files(gsmi_kobj, gsmi_attrs); |
---|
905 | 1037 | out_remove_bin_file: |
---|
906 | 1038 | sysfs_remove_bin_file(gsmi_kobj, &eventlog_bin_attr); |
---|
907 | 1039 | out_err: |
---|
.. | .. |
---|
912 | 1044 | dma_pool_destroy(gsmi_dev.dma_pool); |
---|
913 | 1045 | platform_device_unregister(gsmi_dev.pdev); |
---|
914 | 1046 | pr_info("gsmi: failed to load: %d\n", ret); |
---|
| 1047 | +#ifdef CONFIG_PM |
---|
| 1048 | + platform_driver_unregister(&gsmi_driver_info); |
---|
| 1049 | +#endif |
---|
915 | 1050 | return ret; |
---|
916 | 1051 | } |
---|
917 | 1052 | |
---|
.. | .. |
---|
921 | 1056 | unregister_die_notifier(&gsmi_die_notifier); |
---|
922 | 1057 | atomic_notifier_chain_unregister(&panic_notifier_list, |
---|
923 | 1058 | &gsmi_panic_notifier); |
---|
| 1059 | +#ifdef CONFIG_EFI |
---|
924 | 1060 | efivars_unregister(&efivars); |
---|
| 1061 | +#endif |
---|
925 | 1062 | |
---|
926 | 1063 | sysfs_remove_files(gsmi_kobj, gsmi_attrs); |
---|
927 | 1064 | sysfs_remove_bin_file(gsmi_kobj, &eventlog_bin_attr); |
---|
.. | .. |
---|
931 | 1068 | gsmi_buf_free(gsmi_dev.name_buf); |
---|
932 | 1069 | dma_pool_destroy(gsmi_dev.dma_pool); |
---|
933 | 1070 | platform_device_unregister(gsmi_dev.pdev); |
---|
| 1071 | +#ifdef CONFIG_PM |
---|
| 1072 | + platform_driver_unregister(&gsmi_driver_info); |
---|
| 1073 | +#endif |
---|
934 | 1074 | } |
---|
935 | 1075 | |
---|
936 | 1076 | module_init(gsmi_init); |
---|