diff options
| author | Mike Waychison <mikew@google.com> | 2011-04-29 20:39:19 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-04-29 21:09:34 -0400 |
| commit | 74c5b31c6618f01079212332b2e5f6c42f2d6307 (patch) | |
| tree | f821278fe44849cb10264031159b01c846c7ed9d /drivers/firmware | |
| parent | f548ccd47d608e88d432745091e13f927ced83f7 (diff) | |
driver: Google EFI SMI
The "gsmi" driver bridges userland with firmware specific routines for
accessing hardware.
Currently, this driver only supports NVRAM and eventlog information.
Deprecated functions have been removed from the driver, though their
op-codes are left in place so that they are not re-used.
This driver works by trampolining into the firmware via the smi_command
outlined in the FADT table. Three protocols are used due to various
limitations over time, but all are included herein.
This driver should only ever load on Google boards, identified by either
a "Google, Inc." board vendor string in DMI, or "GOOGLE" in the OEM
strings of the FADT ACPI table. This logic happens in
gsmi_system_valid().
Signed-off-by: Duncan Laurie <dlaurie@google.com>
Signed-off-by: Aaron Durbin <adurbin@google.com>
Signed-off-by: Mike Waychison <mikew@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/firmware')
| -rw-r--r-- | drivers/firmware/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/firmware/Makefile | 2 | ||||
| -rw-r--r-- | drivers/firmware/google/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/firmware/google/Makefile | 2 | ||||
| -rw-r--r-- | drivers/firmware/google/gsmi.c | 940 |
5 files changed, 955 insertions, 0 deletions
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index b3a25a55ba23..efba163595db 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig | |||
| @@ -157,4 +157,6 @@ config SIGMA | |||
| 157 | If unsure, say N here. Drivers that need these helpers will select | 157 | If unsure, say N here. Drivers that need these helpers will select |
| 158 | this option automatically. | 158 | this option automatically. |
| 159 | 159 | ||
| 160 | source "drivers/firmware/google/Kconfig" | ||
| 161 | |||
| 160 | endmenu | 162 | endmenu |
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 00bb0b80a79f..d7d600992d4a 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile | |||
| @@ -13,3 +13,5 @@ obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o | |||
| 13 | obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o | 13 | obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o |
| 14 | obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o | 14 | obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o |
| 15 | obj-$(CONFIG_SIGMA) += sigma.o | 15 | obj-$(CONFIG_SIGMA) += sigma.o |
| 16 | |||
| 17 | obj-y += google/ | ||
diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig new file mode 100644 index 000000000000..4a0383537f5d --- /dev/null +++ b/drivers/firmware/google/Kconfig | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | config GOOGLE_SMI | ||
| 2 | tristate "SMI interface for Google platforms" | ||
| 3 | depends on ACPI && DMI | ||
| 4 | select EFI_VARS | ||
| 5 | help | ||
| 6 | Say Y here if you want to enable SMI callbacks for Google | ||
| 7 | platforms. This provides an interface for writing to and | ||
| 8 | clearing the EFI event log and reading and writing NVRAM | ||
| 9 | variables. | ||
diff --git a/drivers/firmware/google/Makefile b/drivers/firmware/google/Makefile new file mode 100644 index 000000000000..fb127d7b3c7a --- /dev/null +++ b/drivers/firmware/google/Makefile | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | |||
| 2 | obj-$(CONFIG_GOOGLE_SMI) += gsmi.o | ||
diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c new file mode 100644 index 000000000000..fa7f0b3e81dd --- /dev/null +++ b/drivers/firmware/google/gsmi.c | |||
| @@ -0,0 +1,940 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2010 Google Inc. All Rights Reserved. | ||
| 3 | * Author: dlaurie@google.com (Duncan Laurie) | ||
| 4 | * | ||
| 5 | * Re-worked to expose sysfs APIs by mikew@google.com (Mike Waychison) | ||
| 6 | * | ||
| 7 | * EFI SMI interface for Google platforms | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <linux/kernel.h> | ||
| 11 | #include <linux/init.h> | ||
| 12 | #include <linux/types.h> | ||
| 13 | #include <linux/device.h> | ||
| 14 | #include <linux/platform_device.h> | ||
| 15 | #include <linux/errno.h> | ||
| 16 | #include <linux/string.h> | ||
| 17 | #include <linux/spinlock.h> | ||
| 18 | #include <linux/dma-mapping.h> | ||
| 19 | #include <linux/dmapool.h> | ||
| 20 | #include <linux/fs.h> | ||
| 21 | #include <linux/slab.h> | ||
| 22 | #include <linux/ioctl.h> | ||
| 23 | #include <linux/acpi.h> | ||
| 24 | #include <linux/io.h> | ||
| 25 | #include <linux/uaccess.h> | ||
| 26 | #include <linux/dmi.h> | ||
| 27 | #include <linux/kdebug.h> | ||
| 28 | #include <linux/reboot.h> | ||
| 29 | #include <linux/efi.h> | ||
| 30 | |||
| 31 | #define GSMI_SHUTDOWN_CLEAN 0 /* Clean Shutdown */ | ||
| 32 | /* TODO(mikew@google.com): Tie in HARDLOCKUP_DETECTOR with NMIWDT */ | ||
| 33 | #define GSMI_SHUTDOWN_NMIWDT 1 /* NMI Watchdog */ | ||
| 34 | #define GSMI_SHUTDOWN_PANIC 2 /* Panic */ | ||
| 35 | #define GSMI_SHUTDOWN_OOPS 3 /* Oops */ | ||
| 36 | #define GSMI_SHUTDOWN_DIE 4 /* Die -- No longer meaningful */ | ||
| 37 | #define GSMI_SHUTDOWN_MCE 5 /* Machine Check */ | ||
| 38 | #define GSMI_SHUTDOWN_SOFTWDT 6 /* Software Watchdog */ | ||
| 39 | #define GSMI_SHUTDOWN_MBE 7 /* Uncorrected ECC */ | ||
| 40 | #define GSMI_SHUTDOWN_TRIPLE 8 /* Triple Fault */ | ||
| 41 | |||
| 42 | #define DRIVER_VERSION "1.0" | ||
| 43 | #define GSMI_GUID_SIZE 16 | ||
| 44 | #define GSMI_BUF_SIZE 1024 | ||
| 45 | #define GSMI_BUF_ALIGN sizeof(u64) | ||
| 46 | #define GSMI_CALLBACK 0xef | ||
| 47 | |||
| 48 | /* SMI return codes */ | ||
| 49 | #define GSMI_SUCCESS 0x00 | ||
| 50 | #define GSMI_UNSUPPORTED2 0x03 | ||
| 51 | #define GSMI_LOG_FULL 0x0b | ||
| 52 | #define GSMI_VAR_NOT_FOUND 0x0e | ||
| 53 | #define GSMI_HANDSHAKE_SPIN 0x7d | ||
| 54 | #define GSMI_HANDSHAKE_CF 0x7e | ||
| 55 | #define GSMI_HANDSHAKE_NONE 0x7f | ||
| 56 | #define GSMI_INVALID_PARAMETER 0x82 | ||
| 57 | #define GSMI_UNSUPPORTED 0x83 | ||
| 58 | #define GSMI_BUFFER_TOO_SMALL 0x85 | ||
| 59 | #define GSMI_NOT_READY 0x86 | ||
| 60 | #define GSMI_DEVICE_ERROR 0x87 | ||
| 61 | #define GSMI_NOT_FOUND 0x8e | ||
| 62 | |||
| 63 | #define QUIRKY_BOARD_HASH 0x78a30a50 | ||
| 64 | |||
| 65 | /* Internally used commands passed to the firmware */ | ||
| 66 | #define GSMI_CMD_GET_NVRAM_VAR 0x01 | ||
| 67 | #define GSMI_CMD_GET_NEXT_VAR 0x02 | ||
| 68 | #define GSMI_CMD_SET_NVRAM_VAR 0x03 | ||
| 69 | #define GSMI_CMD_SET_EVENT_LOG 0x08 | ||
| 70 | #define GSMI_CMD_CLEAR_EVENT_LOG 0x09 | ||
| 71 | #define GSMI_CMD_CLEAR_CONFIG 0x20 | ||
| 72 | #define GSMI_CMD_HANDSHAKE_TYPE 0xC1 | ||
| 73 | |||
| 74 | /* Magic entry type for kernel events */ | ||
| 75 | #define GSMI_LOG_ENTRY_TYPE_KERNEL 0xDEAD | ||
| 76 | |||
| 77 | /* SMI buffers must be in 32bit physical address space */ | ||
| 78 | struct gsmi_buf { | ||
| 79 | u8 *start; /* start of buffer */ | ||
| 80 | size_t length; /* length of buffer */ | ||
| 81 | dma_addr_t handle; /* dma allocation handle */ | ||
| 82 | u32 address; /* physical address of buffer */ | ||
| 83 | }; | ||
| 84 | |||
| 85 | struct gsmi_device { | ||
| 86 | struct platform_device *pdev; /* platform device */ | ||
| 87 | struct gsmi_buf *name_buf; /* variable name buffer */ | ||
| 88 | struct gsmi_buf *data_buf; /* generic data buffer */ | ||
| 89 | struct gsmi_buf *param_buf; /* parameter buffer */ | ||
| 90 | spinlock_t lock; /* serialize access to SMIs */ | ||
| 91 | u16 smi_cmd; /* SMI command port */ | ||
| 92 | int handshake_type; /* firmware handler interlock type */ | ||
| 93 | struct dma_pool *dma_pool; /* DMA buffer pool */ | ||
| 94 | } gsmi_dev; | ||
| 95 | |||
| 96 | /* Packed structures for communicating with the firmware */ | ||
| 97 | struct gsmi_nvram_var_param { | ||
| 98 | efi_guid_t guid; | ||
| 99 | u32 name_ptr; | ||
| 100 | u32 attributes; | ||
| 101 | u32 data_len; | ||
| 102 | u32 data_ptr; | ||
| 103 | } __packed; | ||
| 104 | |||
| 105 | struct gsmi_get_next_var_param { | ||
| 106 | u8 guid[GSMI_GUID_SIZE]; | ||
| 107 | u32 name_ptr; | ||
| 108 | u32 name_len; | ||
| 109 | } __packed; | ||
| 110 | |||
| 111 | struct gsmi_set_eventlog_param { | ||
| 112 | u32 data_ptr; | ||
| 113 | u32 data_len; | ||
| 114 | u32 type; | ||
| 115 | } __packed; | ||
| 116 | |||
| 117 | /* Event log formats */ | ||
| 118 | struct gsmi_log_entry_type_1 { | ||
| 119 | u16 type; | ||
| 120 | u32 instance; | ||
| 121 | } __packed; | ||
| 122 | |||
| 123 | |||
| 124 | /* | ||
| 125 | * Some platforms don't have explicit SMI handshake | ||
| 126 | * and need to wait for SMI to complete. | ||
| 127 | */ | ||
| 128 | #define GSMI_DEFAULT_SPINCOUNT 0x10000 | ||
| 129 | static unsigned int spincount = GSMI_DEFAULT_SPINCOUNT; | ||
| 130 | module_param(spincount, uint, 0600); | ||
| 131 | MODULE_PARM_DESC(spincount, | ||
| 132 | "The number of loop iterations to use when using the spin handshake."); | ||
| 133 | |||
| 134 | static struct gsmi_buf *gsmi_buf_alloc(void) | ||
| 135 | { | ||
| 136 | struct gsmi_buf *smibuf; | ||
| 137 | |||
| 138 | smibuf = kzalloc(sizeof(*smibuf), GFP_KERNEL); | ||
| 139 | if (!smibuf) { | ||
| 140 | printk(KERN_ERR "gsmi: out of memory\n"); | ||
| 141 | return NULL; | ||
| 142 | } | ||
| 143 | |||
| 144 | /* allocate buffer in 32bit address space */ | ||
| 145 | smibuf->start = dma_pool_alloc(gsmi_dev.dma_pool, GFP_KERNEL, | ||
| 146 | &smibuf->handle); | ||
| 147 | if (!smibuf->start) { | ||
| 148 | printk(KERN_ERR "gsmi: failed to allocate name buffer\n"); | ||
| 149 | kfree(smibuf); | ||
