diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-19 21:24:11 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-19 21:24:11 -0400 |
commit | 39ab05c8e0b519ff0a04a869f065746e6e8c3d95 (patch) | |
tree | e73f0ba74c4ea7a80dff9b2dd9445a3a74190e28 /drivers/firmware | |
parent | 1477fcc290b3d5c2614bde98bf3b1154c538860d (diff) | |
parent | c42d2237143fcf35cff642cefe2bcf7786aae312 (diff) |
Merge branch 'driver-core-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6
* 'driver-core-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core-2.6: (44 commits)
debugfs: Silence DEBUG_STRICT_USER_COPY_CHECKS=y warning
sysfs: remove "last sysfs file:" line from the oops messages
drivers/base/memory.c: fix warning due to "memory hotplug: Speed up add/remove when blocks are larger than PAGES_PER_SECTION"
memory hotplug: Speed up add/remove when blocks are larger than PAGES_PER_SECTION
SYSFS: Fix erroneous comments for sysfs_update_group().
driver core: remove the driver-model structures from the documentation
driver core: Add the device driver-model structures to kerneldoc
Translated Documentation/email-clients.txt
RAW driver: Remove call to kobject_put().
reboot: disable usermodehelper to prevent fs access
efivars: prevent oops on unload when efi is not enabled
Allow setting of number of raw devices as a module parameter
Introduce CONFIG_GOOGLE_FIRMWARE
driver: Google Memory Console
driver: Google EFI SMI
x86: Better comments for get_bios_ebda()
x86: get_bios_ebda_length()
misc: fix ti-st build issues
params.c: Use new strtobool function to process boolean inputs
debugfs: move to new strtobool
...
Fix up trivial conflicts in fs/debugfs/file.c due to the same patch
being applied twice, and an unrelated cleanup nearby.
Diffstat (limited to 'drivers/firmware')
-rw-r--r-- | drivers/firmware/Kconfig | 2 | ||||
-rw-r--r-- | drivers/firmware/Makefile | 2 | ||||
-rw-r--r-- | drivers/firmware/efivars.c | 21 | ||||
-rw-r--r-- | drivers/firmware/google/Kconfig | 31 | ||||
-rw-r--r-- | drivers/firmware/google/Makefile | 3 | ||||
-rw-r--r-- | drivers/firmware/google/gsmi.c | 940 | ||||
-rw-r--r-- | drivers/firmware/google/memconsole.c | 166 |
7 files changed, 1159 insertions, 6 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..47338c979126 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-$(CONFIG_GOOGLE_FIRMWARE) += google/ | ||
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index ff0c373e3bbf..a2d2f1f0d4f3 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c | |||
@@ -677,8 +677,8 @@ create_efivars_bin_attributes(struct efivars *efivars) | |||
677 | 677 | ||
678 | return 0; | 678 | return 0; |
679 | out_free: | 679 | out_free: |
680 | kfree(efivars->new_var); | 680 | kfree(efivars->del_var); |
681 | efivars->new_var = NULL; | 681 | efivars->del_var = NULL; |
682 | kfree(efivars->new_var); | 682 | kfree(efivars->new_var); |
683 | efivars->new_var = NULL; | 683 | efivars->new_var = NULL; |
684 | return error; | 684 | return error; |
@@ -803,6 +803,8 @@ efivars_init(void) | |||
803 | ops.set_variable = efi.set_variable; | 803 | ops.set_variable = efi.set_variable; |
804 | ops.get_next_variable = efi.get_next_variable; | 804 | ops.get_next_variable = efi.get_next_variable; |
805 | error = register_efivars(&__efivars, &ops, efi_kobj); | 805 | error = register_efivars(&__efivars, &ops, efi_kobj); |
806 | if (error) | ||
807 | goto err_put; | ||
806 | 808 | ||
807 | /* Don't forget the systab entry */ | 809 | /* Don't forget the systab entry */ |
808 | error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group); | 810 | error = sysfs_create_group(efi_kobj, &efi_subsys_attr_group); |
@@ -810,18 +812,25 @@ efivars_init(void) | |||
810 | printk(KERN_ERR | 812 | printk(KERN_ERR |
811 | "efivars: Sysfs attribute export failed with error %d.\n", | 813 | "efivars: Sysfs attribute export failed with error %d.\n", |
812 | error); | 814 | error); |
813 | unregister_efivars(&__efivars); | 815 | goto err_unregister; |
814 | kobject_put(efi_kobj); | ||
815 | } | 816 | } |
816 | 817 | ||
818 | return 0; | ||
819 | |||
820 | err_unregister: | ||
821 | unregister_efivars(&__efivars); | ||
822 | err_put: | ||
823 | kobject_put(efi_kobj); | ||
817 | return error; | 824 | return error; |
818 | } | 825 | } |
819 | 826 | ||
820 | static void __exit | 827 | static void __exit |
821 | efivars_exit(void) | 828 | efivars_exit(void) |
822 | { | 829 | { |
823 | unregister_efivars(&__efivars); | 830 | if (efi_enabled) { |
824 | kobject_put(efi_kobj); | 831 | unregister_efivars(&__efivars); |
832 | kobject_put(efi_kobj); | ||
833 | } | ||
825 | } | 834 | } |
826 | 835 | ||
827 | module_init(efivars_init); | 836 | module_init(efivars_init); |
diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig new file mode 100644 index 000000000000..87096b6ca5c9 --- /dev/null +++ b/drivers/firmware/google/Kconfig | |||
@@ -0,0 +1,31 @@ | |||
1 | config GOOGLE_FIRMWARE | ||
2 | bool "Google Firmware Drivers" | ||
3 | depends on X86 | ||
4 | default n | ||
5 | help | ||
6 | These firmware drivers are used by Google's servers. They are | ||
7 | only useful if you are working directly on one of their | ||
8 | proprietary servers. If in doubt, say "N". | ||
9 | |||
10 | menu "Google Firmware Drivers" | ||
11 | depends on GOOGLE_FIRMWARE | ||
12 | |||
13 | config GOOGLE_SMI | ||
14 | tristate "SMI interface for Google platforms" | ||
15 | depends on ACPI && DMI | ||
16 | select EFI_VARS | ||
17 | help | ||
18 | Say Y here if you want to enable SMI callbacks for Google | ||
19 | platforms. This provides an interface for writing to and | ||
20 | clearing the EFI event log and reading and writing NVRAM | ||
21 | variables. | ||
22 | |||
23 | config GOOGLE_MEMCONSOLE | ||
24 | tristate "Firmware Memory Console" | ||
25 | depends on DMI | ||
26 | help | ||
27 | This option enables the kernel to search for a firmware log in | ||
28 | the EBDA on Google servers. If found, this log is exported to | ||
29 | userland in the file /sys/firmware/log. | ||
30 | |||
31 | endmenu | ||
diff --git a/drivers/firmware/google/Makefile b/drivers/firmware/google/Makefile new file mode 100644 index 000000000000..54a294e3cb61 --- /dev/null +++ b/drivers/firmware/google/Makefile | |||
@@ -0,0 +1,3 @@ | |||
1 | |||
2 | obj-$(CONFIG_GOOGLE_SMI) += gsmi.o | ||
3 | obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.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); | ||
150 | return NULL; | ||
151 | } | ||
152 | |||
153 | /* fill in the buffer handle */ | ||
154 | smibuf->length = GSMI_BUF_SIZE; | ||
155 | smibuf->address = (u32)virt_to_phys(smibuf->start); | ||
156 | |||
157 | return smibuf; | ||
158 | } | ||
159 | |||
160 | static void gsmi_buf_free(struct gsmi_buf *smibuf) | ||
161 | { | ||
162 | if (smibuf) { | ||
163 | if (smibuf->start) | ||
164 | dma_pool_free(gsmi_dev.dma_pool, smibuf->start, | ||
165 | smibuf->handle); | ||
166 | kfree(smibuf); | ||
167 | } | ||
168 | } | ||
169 | |||
170 | /* | ||
171 | * Make a call to gsmi func(sub). GSMI error codes are translated to | ||
172 | * in-kernel errnos (0 on success, -ERRNO on error). | ||
173 | */ | ||
174 | static int gsmi_exec(u8 func, u8 sub) | ||
175 | { | ||
176 | u16 cmd = (sub << 8) | func; | ||
177 | u16 result = 0; | ||
178 | int rc = 0; | ||
179 | |||
180 | /* | ||
181 | * AH : Subfunction number | ||
182 | * AL : Function number | ||
183 | * EBX : Parameter block address | ||
184 | * DX : SMI command port | ||
185 | * | ||
186 | * Three protocols here. See also the comment in gsmi_init(). | ||
187 | */ | ||
188 | if (gsmi_dev.handshake_type == GSMI_HANDSHAKE_CF) { | ||
189 | /* | ||
190 | * If handshake_type == HANDSHAKE_CF then set CF on the | ||
191 | * way in and wait for the handler to clear it; this avoids | ||
192 | * corrupting register state on those chipsets which have | ||
193 | * a delay between writing the SMI trigger register and | ||
194 | * entering SMM. | ||
195 | */ | ||
196 | asm volatile ( | ||
197 | "stc\n" | ||
198 | "outb %%al, %%dx\n" | ||
199 | "1: jc 1b\n" | ||
200 | : "=a" (result) | ||
201 | : "0" (cmd), | ||
202 | "d" (gsmi_dev.smi_cmd), | ||
203 | "b" (gsmi_dev.param_buf->address) | ||
204 | : "memory", "cc" | ||
205 | ); | ||
206 | } else if (gsmi_dev.handshake_type == GSMI_HANDSHAKE_SPIN) { | ||
207 | /* | ||
208 | * If handshake_type == HANDSHAKE_SPIN we spin a | ||
209 | * hundred-ish usecs to ensure the SMI has triggered. | ||
210 | */ | ||
211 | asm volatile ( | ||
212 | "outb %%al, %%dx\n" | ||
213 | "1: loop 1b\n" | ||
214 | : "=a" (result) | ||
215 | : "0" (cmd), | ||
216 | "d" (gsmi_dev.smi_cmd), | ||
217 | "b" (gsmi_dev.param_buf->address), | ||
218 | "c" (spincount) | ||
219 | : "memory", "cc" | ||
220 | ); | ||
221 | } else { | ||
222 | /* | ||
223 | * If handshake_type == HANDSHAKE_NONE we do nothing; | ||
224 | * either we don't need to or it's legacy firmware that | ||
225 | * doesn't understand the CF protocol. | ||
226 | */ | ||
227 | asm volatile ( | ||
228 | "outb %%al, %%dx\n\t" | ||
229 | : "=a" (result) | ||
230 | : "0" (cmd), | ||
231 | "d" (gsmi_dev.smi_cmd), | ||
232 | "b" (gsmi_dev.param_buf->address) | ||
233 | : "memory", "cc" | ||
234 | ); | ||
235 | } | ||
236 | |||
237 | /* check return code from SMI handler */ | ||
238 | switch (result) { | ||
239 | case GSMI_SUCCESS: | ||
240 | break; | ||
241 | case GSMI_VAR_NOT_FOUND: | ||
242 | /* not really an error, but let the caller know */ | ||
243 | rc = 1; | ||
244 | break; | ||
245 | case GSMI_INVALID_PARAMETER: | ||
246 | printk(KERN_ERR "gsmi: exec 0x%04x: Invalid parameter\n", cmd); | ||
247 | rc = -EINVAL; | ||
248 | break; | ||
249 | case GSMI_BUFFER_TOO_SMALL: | ||
250 | printk(KERN_ERR "gsmi: exec 0x%04x: Buffer too small\n", cmd); | ||
251 | rc = -ENOMEM; | ||
252 | break; | ||
253 | case GSMI_UNSUPPORTED: | ||
254 | case GSMI_UNSUPPORTED2: | ||
255 | if (sub != GSMI_CMD_HANDSHAKE_TYPE) | ||
256 | printk(KERN_ERR "gsmi: exec 0x%04x: Not supported\n", | ||
257 | cmd); | ||
258 | rc = -ENOSYS; | ||
259 | break; | ||
260 | case GSMI_NOT_READY: | ||
261 | printk(KERN_ERR "gsmi: exec 0x%04x: Not ready\n", cmd); | ||
262 | rc = -EBUSY; | ||
263 | break; | ||
264 | case GSMI_DEVICE_ERROR: | ||
265 | printk(KERN_ERR "gsmi: exec 0x%04x: Device error\n", cmd); | ||
266 | rc = -EFAULT; | ||
267 | break; | ||
268 | case GSMI_NOT_FOUND: | ||
269 | printk(KERN_ERR "gsmi: exec 0x%04x: Data not found\n", cmd); | ||
270 | rc = -ENOENT; | ||
271 | break; | ||
272 | case GSMI_LOG_FULL: | ||
273 | printk(KERN_ERR "gsmi: exec 0x%04x: Log full\n", cmd); | ||
274 | rc = -ENOSPC; | ||
275 | break; | ||
276 | case GSMI_HANDSHAKE_CF: | ||
277 | case GSMI_HANDSHAKE_SPIN: | ||
278 | case GSMI_HANDSHAKE_NONE: | ||
279 | rc = result; | ||
280 | break; | ||
281 | default: | ||
282 | printk(KERN_ERR "gsmi: exec 0x%04x: Unknown error 0x%04x\n", | ||
283 | cmd, result); | ||
284 | rc = -ENXIO; | ||
285 | } | ||
286 | |||
287 | return rc; | ||
288 | } | ||
289 | |||
290 | /* Return the number of unicode characters in data */ | ||
291 | static size_t | ||
292 | utf16_strlen(efi_char16_t *data, unsigned long maxlength) | ||
293 | { | ||
294 | unsigned long length = 0; | ||
295 | |||
296 | while (*data++ != 0 && length < maxlength) | ||
297 | length++; | ||
298 | return length; | ||
299 | } | ||
300 | |||
301 | static efi_status_t gsmi_get_variable(efi_char16_t *name, | ||
302 | efi_guid_t *vendor, u32 *attr, | ||
303 | unsigned long *data_size, | ||
304 | void *data) | ||
305 | { | ||
306 | struct gsmi_nvram_var_param param = { | ||
307 | .name_ptr = gsmi_dev.name_buf->address, | ||
308 | .data_ptr = gsmi_dev.data_buf->address, | ||
309 | .data_len = (u32)*data_size, | ||
310 | }; | ||
311 | efi_status_t ret = EFI_SUCCESS; | ||
312 | unsigned long flags; | ||
313 | size_t name_len = utf16_strlen(name, GSMI_BUF_SIZE / 2); | ||
314 | int rc; | ||
315 | |||
316 | if (name_len >= GSMI_BUF_SIZE / 2) | ||
317 | return EFI_BAD_BUFFER_SIZE; | ||
318 | |||
319 | spin_lock_irqsave(&gsmi_dev.lock, flags); | ||
320 | |||
321 | /* Vendor guid */ | ||
322 | memcpy(¶m.guid, vendor, sizeof(param.guid)); | ||
323 | |||
324 | /* variable name, already in UTF-16 */ | ||
325 | memset(gsmi_dev.name_buf->start, 0, gsmi_dev.name_buf->length); | ||
326 | memcpy(gsmi_dev.name_buf->start, name, name_len * 2); | ||
327 | |||
328 | /* data pointer */ | ||
329 | memset(gsmi_dev.data_buf->start, 0, gsmi_dev.data_buf->length); | ||
330 | |||
331 | /* parameter buffer */ | ||
332 | memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length); | ||
333 | memcpy(gsmi_dev.param_buf->start, ¶m, sizeof(param)); | ||
334 | |||
335 | rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_GET_NVRAM_VAR); | ||
336 | if (rc < 0) { | ||
337 | printk(KERN_ERR "gsmi: Get Variable failed\n"); | ||
338 | ret = EFI_LOAD_ERROR; | ||
339 | } else if (rc == 1) { | ||
340 | /* variable was not found */ | ||
341 | ret = EFI_NOT_FOUND; | ||
342 | } else { | ||
343 | /* Get the arguments back */ | ||
344 | memcpy(¶m, gsmi_dev.param_buf->start, sizeof(param)); | ||
345 | |||
346 | /* The size reported is the min of all of our buffers */ | ||
347 | *data_size = min(*data_size, gsmi_dev.data_buf->length); | ||
348 | *data_size = min_t(unsigned long, *data_size, param.data_len); | ||
349 | |||
350 | /* Copy data back to return buffer. */ | ||
351 | memcpy(data, gsmi_dev.data_buf->start, *data_size); | ||
352 | |||
353 | /* All variables are have the following attributes */ | ||
354 | *attr = EFI_VARIABLE_NON_VOLATILE | | ||
355 | EFI_VARIABLE_BOOTSERVICE_ACCESS | | ||
356 | EFI_VARIABLE_RUNTIME_ACCESS; | ||
357 | } | ||
358 | |||
359 | spin_unlock_irqrestore(&gsmi_dev.lock, flags); | ||
360 | |||
361 | return ret; | ||
362 | } | ||
363 | |||
364 | static efi_status_t gsmi_get_next_variable(unsigned long *name_size, | ||
365 | efi_char16_t *name, | ||
366 | efi_guid_t *vendor) | ||
367 | { | ||
368 | struct gsmi_get_next_var_param param = { | ||
369 | .name_ptr = gsmi_dev.name_buf->address, | ||
370 | .name_len = gsmi_dev.name_buf->length, | ||
371 | }; | ||
372 | efi_status_t ret = EFI_SUCCESS; | ||
373 | int rc; | ||
374 | unsigned long flags; | ||
375 | |||
376 | /* For the moment, only support buffers that exactly match in size */ | ||
377 | if (*name_size != GSMI_BUF_SIZE) | ||
378 | return EFI_BAD_BUFFER_SIZE; | ||
379 | |||
380 | /* Let's make sure the thing is at least null-terminated */ | ||
381 | if (utf16_strlen(name, GSMI_BUF_SIZE / 2) == GSMI_BUF_SIZE / 2) | ||
382 | return EFI_INVALID_PARAMETER; | ||
383 | |||
384 | spin_lock_irqsave(&gsmi_dev.lock, flags); | ||
385 | |||
386 | /* guid */ | ||
387 | memcpy(¶m.guid, vendor, sizeof(param.guid)); | ||
388 | |||
389 | /* variable name, already in UTF-16 */ | ||
390 | memcpy(gsmi_dev.name_buf->start, name, *name_size); | ||
391 | |||
392 | /* parameter buffer */ | ||
393 | memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length); | ||
394 | memcpy(gsmi_dev.param_buf->start, ¶m, sizeof(param)); | ||
395 | |||
396 | rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_GET_NEXT_VAR); | ||
397 | if (rc < 0) { | ||
398 | printk(KERN_ERR "gsmi: Get Next Variable Name failed\n"); | ||
399 | ret = EFI_LOAD_ERROR; | ||
400 | } else if (rc == 1) { | ||
401 | /* variable not found -- end of list */ | ||
402 | ret = EFI_NOT_FOUND; | ||
403 | } else { | ||
404 | /* copy variable data back to return buffer */ | ||
405 | memcpy(¶m, gsmi_dev.param_buf->start, sizeof(param)); | ||
406 | |||
407 | /* Copy the name back */ | ||
408 | memcpy(name, gsmi_dev.name_buf->start, GSMI_BUF_SIZE); | ||
409 | *name_size = utf16_strlen(name, GSMI_BUF_SIZE / 2) * 2; | ||
410 | |||
411 | /* copy guid to return buffer */ | ||
412 | memcpy(vendor, ¶m.guid, sizeof(param.guid)); | ||
413 | ret = EFI_SUCCESS; | ||
414 | } | ||
415 | |||
416 | spin_unlock_irqrestore(&gsmi_dev.lock, flags); | ||
417 | |||
418 | return ret; | ||
419 | } | ||
420 | |||
421 | static efi_status_t gsmi_set_variable(efi_char16_t *name, | ||
422 | efi_guid_t *vendor, | ||
423 | unsigned long attr, | ||
424 | unsigned long data_size, | ||
425 | void *data) | ||
426 | { | ||
427 | struct gsmi_nvram_var_param param = { | ||
428 | .name_ptr = gsmi_dev.name_buf->address, | ||
429 | .data_ptr = gsmi_dev.data_buf->address, | ||
430 | .data_len = (u32)data_size, | ||
431 | .attributes = EFI_VARIABLE_NON_VOLATILE | | ||
432 | EFI_VARIABLE_BOOTSERVICE_ACCESS | | ||
433 | EFI_VARIABLE_RUNTIME_ACCESS, | ||
434 | }; | ||
435 | size_t name_len = utf16_strlen(name, GSMI_BUF_SIZE / 2); | ||
436 | efi_status_t ret = EFI_SUCCESS; | ||
437 | int rc; | ||
438 | unsigned long flags; | ||
439 | |||
440 | if (name_len >= GSMI_BUF_SIZE / 2) | ||
441 | return EFI_BAD_BUFFER_SIZE; | ||
442 | |||
443 | spin_lock_irqsave(&gsmi_dev.lock, flags); | ||
444 | |||
445 | /* guid */ | ||
446 | memcpy(¶m.guid, vendor, sizeof(param.guid)); | ||
447 | |||
448 | /* variable name, already in UTF-16 */ | ||
449 | memset(gsmi_dev.name_buf->start, 0, gsmi_dev.name_buf->length); | ||
450 | memcpy(gsmi_dev.name_buf->start, name, name_len * 2); | ||
451 | |||
452 | /* data pointer */ | ||
453 | memset(gsmi_dev.data_buf->start, 0, gsmi_dev.data_buf->length); | ||
454 | memcpy(gsmi_dev.data_buf->start, data, data_size); | ||
455 | |||
456 | /* parameter buffer */ | ||
457 | memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length); | ||
458 | memcpy(gsmi_dev.param_buf->start, ¶m, sizeof(param)); | ||
459 | |||
460 | rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_SET_NVRAM_VAR); | ||
461 | if (rc < 0) { | ||
462 | printk(KERN_ERR "gsmi: Set Variable failed\n"); | ||
463 | ret = EFI_INVALID_PARAMETER; | ||
464 | } | ||
465 | |||
466 | spin_unlock_irqrestore(&gsmi_dev.lock, flags); | ||
467 | |||
468 | return ret; | ||
469 | } | ||
470 | |||
471 | static const struct efivar_operations efivar_ops = { | ||
472 | .get_variable = gsmi_get_variable, | ||
473 | .set_variable = gsmi_set_variable, | ||
474 | .get_next_variable = gsmi_get_next_variable, | ||
475 | }; | ||
476 | |||
477 | static ssize_t eventlog_write(struct file *filp, struct kobject *kobj, | ||
478 | struct bin_attribute *bin_attr, | ||
479 | char *buf, loff_t pos, size_t count) | ||
480 | { | ||
481 | struct gsmi_set_eventlog_param param = { | ||
482 | .data_ptr = gsmi_dev.data_buf->address, | ||
483 | }; | ||
484 | int rc = 0; | ||
485 | unsigned long flags; | ||
486 | |||
487 | /* Pull the type out */ | ||
488 | if (count < sizeof(u32)) | ||
489 | return -EINVAL; | ||
490 | param.type = *(u32 *)buf; | ||
491 | count -= sizeof(u32); | ||
492 | buf += sizeof(u32); | ||
493 | |||
494 | /* The remaining buffer is the data payload */ | ||
495 | if (count > gsmi_dev.data_buf->length) | ||
496 | return -EINVAL; | ||
497 | param.data_len = count - sizeof(u32); | ||
498 | |||
499 | spin_lock_irqsave(&gsmi_dev.lock, flags); | ||
500 | |||
501 | /* data pointer */ | ||
502 | memset(gsmi_dev.data_buf->start, 0, gsmi_dev.data_buf->length); | ||
503 | memcpy(gsmi_dev.data_buf->start, buf, param.data_len); | ||
504 | |||
505 | /* parameter buffer */ | ||
506 | memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length); | ||
507 | memcpy(gsmi_dev.param_buf->start, ¶m, sizeof(param)); | ||
508 | |||
509 | rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_SET_EVENT_LOG); | ||
510 | if (rc < 0) | ||
511 | printk(KERN_ERR "gsmi: Set Event Log failed\n"); | ||
512 | |||
513 | spin_unlock_irqrestore(&gsmi_dev.lock, flags); | ||
514 | |||
515 | return rc; | ||
516 | |||
517 | } | ||
518 | |||
519 | static struct bin_attribute eventlog_bin_attr = { | ||
520 | .attr = {.name = "append_to_eventlog", .mode = 0200}, | ||
521 | .write = eventlog_write, | ||
522 | }; | ||
523 | |||
524 | static ssize_t gsmi_clear_eventlog_store(struct kobject *kobj, | ||
525 | struct kobj_attribute *attr, | ||
526 | const char *buf, size_t count) | ||
527 | { | ||
528 | int rc; | ||
529 | unsigned long flags; | ||
530 | unsigned long val; | ||
531 | struct { | ||
532 | u32 percentage; | ||
533 | u32 data_type; | ||
534 | } param; | ||
535 | |||
536 | rc = strict_strtoul(buf, 0, &val); | ||
537 | if (rc) | ||
538 | return rc; | ||
539 | |||
540 | /* | ||
541 | * Value entered is a percentage, 0 through 100, anything else | ||
542 | * is invalid. | ||
543 | */ | ||
544 | if (val > 100) | ||
545 | return -EINVAL; | ||
546 | |||
547 | /* data_type here selects the smbios event log. */ | ||
548 | param.percentage = val; | ||
549 | param.data_type = 0; | ||
550 | |||
551 | spin_lock_irqsave(&gsmi_dev.lock, flags); | ||
552 | |||
553 | /* parameter buffer */ | ||
554 | memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length); | ||
555 | memcpy(gsmi_dev.param_buf->start, ¶m, sizeof(param)); | ||
556 | |||
557 | rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_CLEAR_EVENT_LOG); | ||
558 | |||
559 | spin_unlock_irqrestore(&gsmi_dev.lock, flags); | ||
560 | |||
561 | if (rc) | ||
562 | return rc; | ||
563 | return count; | ||
564 | } | ||
565 | |||
566 | static struct kobj_attribute gsmi_clear_eventlog_attr = { | ||
567 | .attr = {.name = "clear_eventlog", .mode = 0200}, | ||
568 | .store = gsmi_clear_eventlog_store, | ||
569 | }; | ||
570 | |||
571 | static ssize_t gsmi_clear_config_store(struct kobject *kobj, | ||
572 | struct kobj_attribute *attr, | ||
573 | const char *buf, size_t count) | ||
574 | { | ||
575 | int rc; | ||
576 | unsigned long flags; | ||
577 | |||
578 | spin_lock_irqsave(&gsmi_dev.lock, flags); | ||
579 | |||
580 | /* clear parameter buffer */ | ||
581 | memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length); | ||
582 | |||
583 | rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_CLEAR_CONFIG); | ||
584 | |||
585 | spin_unlock_irqrestore(&gsmi_dev.lock, flags); | ||
586 | |||
587 | if (rc) | ||
588 | return rc; | ||
589 | return count; | ||
590 | } | ||
591 | |||
592 | static struct kobj_attribute gsmi_clear_config_attr = { | ||
593 | .attr = {.name = "clear_config", .mode = 0200}, | ||
594 | .store = gsmi_clear_config_store, | ||
595 | }; | ||
596 | |||
597 | static const struct attribute *gsmi_attrs[] = { | ||
598 | &gsmi_clear_config_attr.attr, | ||
599 | &gsmi_clear_eventlog_attr.attr, | ||
600 | NULL, | ||
601 | }; | ||
602 | |||
603 | static int gsmi_shutdown_reason(int reason) | ||
604 | { | ||
605 | struct gsmi_log_entry_type_1 entry = { | ||
606 | .type = GSMI_LOG_ENTRY_TYPE_KERNEL, | ||
607 | .instance = reason, | ||
608 | }; | ||
609 | struct gsmi_set_eventlog_param param = { | ||
610 | .data_len = sizeof(entry), | ||
611 | .type = 1, | ||
612 | }; | ||
613 | static int saved_reason; | ||
614 | int rc = 0; | ||
615 | unsigned long flags; | ||
616 | |||
617 | /* avoid duplicate entries in the log */ | ||
618 | if (saved_reason & (1 << reason)) | ||
619 | return 0; | ||
620 | |||
621 | spin_lock_irqsave(&gsmi_dev.lock, flags); | ||
622 | |||
623 | saved_reason |= (1 << reason); | ||
624 | |||
625 | /* data pointer */ | ||
626 | memset(gsmi_dev.data_buf->start, 0, gsmi_dev.data_buf->length); | ||
627 | memcpy(gsmi_dev.data_buf->start, &entry, sizeof(entry)); | ||
628 | |||
629 | /* parameter buffer */ | ||
630 | param.data_ptr = gsmi_dev.data_buf->address; | ||
631 | memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length); | ||
632 | memcpy(gsmi_dev.param_buf->start, ¶m, sizeof(param)); | ||
633 | |||
634 | rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_SET_EVENT_LOG); | ||
635 | |||
636 | spin_unlock_irqrestore(&gsmi_dev.lock, flags); | ||
637 | |||
638 | if (rc < 0) | ||
639 | printk(KERN_ERR "gsmi: Log Shutdown Reason failed\n"); | ||
640 | else | ||
641 | printk(KERN_EMERG "gsmi: Log Shutdown Reason 0x%02x\n", | ||
642 | reason); | ||
643 | |||
644 | return rc; | ||
645 | } | ||
646 | |||
647 | static int gsmi_reboot_callback(struct notifier_block *nb, | ||
648 | unsigned long reason, void *arg) | ||
649 | { | ||
650 | gsmi_shutdown_reason(GSMI_SHUTDOWN_CLEAN); | ||
651 | return NOTIFY_DONE; | ||
652 | } | ||
653 | |||
654 | static struct notifier_block gsmi_reboot_notifier = { | ||
655 | .notifier_call = gsmi_reboot_callback | ||
656 | }; | ||
657 | |||
658 | static int gsmi_die_callback(struct notifier_block *nb, | ||
659 | unsigned long reason, void *arg) | ||
660 | { | ||
661 | if (reason == DIE_OOPS) | ||
662 | gsmi_shutdown_reason(GSMI_SHUTDOWN_OOPS); | ||
663 | return NOTIFY_DONE; | ||
664 | } | ||
665 | |||
666 | static struct notifier_block gsmi_die_notifier = { | ||
667 | .notifier_call = gsmi_die_callback | ||
668 | }; | ||
669 | |||
670 | static int gsmi_panic_callback(struct notifier_block *nb, | ||
671 | unsigned long reason, void *arg) | ||
672 | { | ||
673 | gsmi_shutdown_reason(GSMI_SHUTDOWN_PANIC); | ||
674 | return NOTIFY_DONE; | ||
675 | } | ||
676 | |||
677 | static struct notifier_block gsmi_panic_notifier = { | ||
678 | .notifier_call = gsmi_panic_callback, | ||
679 | }; | ||
680 | |||
681 | /* | ||
682 | * This hash function was blatantly copied from include/linux/hash.h. | ||
683 | * It is used by this driver to obfuscate a board name that requires a | ||
684 | * quirk within this driver. | ||
685 | * | ||
686 | * Please do not remove this copy of the function as any changes to the | ||
687 | * global utility hash_64() function would break this driver's ability | ||
688 | * to identify a board and provide the appropriate quirk -- mikew@google.com | ||
689 | */ | ||
690 | static u64 __init local_hash_64(u64 val, unsigned bits) | ||
691 | { | ||
692 | u64 hash = val; | ||
693 | |||
694 | /* Sigh, gcc can't optimise this alone like it does for 32 bits. */ | ||
695 | u64 n = hash; | ||
696 | n <<= 18; | ||
697 | hash -= n; | ||
698 | n <<= 33; | ||
699 | hash -= n; | ||
700 | n <<= 3; | ||
701 | hash += n; | ||
702 | n <<= 3; | ||
703 | hash -= n; | ||
704 | n <<= 4; | ||
705 | hash += n; | ||
706 | n <<= 2; | ||
707 | hash += n; | ||
708 | |||
709 | /* High bits are more random, so use them. */ | ||
710 | return hash >> (64 - bits); | ||
711 | } | ||
712 | |||
713 | static u32 __init hash_oem_table_id(char s[8]) | ||
714 | { | ||
715 | u64 input; | ||
716 | memcpy(&input, s, 8); | ||
717 | return local_hash_64(input, 32); | ||
718 | } | ||
719 | |||
720 | static struct dmi_system_id gsmi_dmi_table[] __initdata = { | ||
721 | { | ||
722 | .ident = "Google Board", | ||
723 | .matches = { | ||
724 | DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."), | ||
725 | }, | ||
726 | }, | ||
727 | {} | ||
728 | }; | ||
729 | MODULE_DEVICE_TABLE(dmi, gsmi_dmi_table); | ||
730 | |||
731 | static __init int gsmi_system_valid(void) | ||
732 | { | ||
733 | u32 hash; | ||
734 | |||
735 | if (!dmi_check_system(gsmi_dmi_table)) | ||
736 | return -ENODEV; | ||
737 | |||
738 | /* | ||
739 | * Only newer firmware supports the gsmi interface. All older | ||
740 | * firmware that didn't support this interface used to plug the | ||
741 | * table name in the first four bytes of the oem_table_id field. | ||
742 | * Newer firmware doesn't do that though, so use that as the | ||
743 | * discriminant factor. We have to do this in order to | ||
744 | * whitewash our board names out of the public driver. | ||
745 | */ | ||
746 | if (!strncmp(acpi_gbl_FADT.header.oem_table_id, "FACP", 4)) { | ||
747 | printk(KERN_INFO "gsmi: Board is too old\n"); | ||
748 | return -ENODEV; | ||
749 | } | ||
750 | |||
751 | /* Disable on board with 1.0 BIOS due to Google bug 2602657 */ | ||
752 | hash = hash_oem_table_id(acpi_gbl_FADT.header.oem_table_id); | ||
753 | if (hash == QUIRKY_BOARD_HASH) { | ||
754 | const char *bios_ver = dmi_get_system_info(DMI_BIOS_VERSION); | ||
755 | if (strncmp(bios_ver, "1.0", 3) == 0) { | ||
756 | pr_info("gsmi: disabled on this board's BIOS %s\n", | ||
757 | bios_ver); | ||
758 | return -ENODEV; | ||
759 | } | ||
760 | } | ||
761 | |||
762 | /* check for valid SMI command port in ACPI FADT */ | ||
763 | if (acpi_gbl_FADT.smi_command == 0) { | ||
764 | pr_info("gsmi: missing smi_command\n"); | ||
765 | return -ENODEV; | ||
766 | } | ||
767 | |||
768 | /* Found */ | ||
769 | return 0; | ||
770 | } | ||
771 | |||
772 | static struct kobject *gsmi_kobj; | ||
773 | static struct efivars efivars; | ||
774 | |||
775 | static __init int gsmi_init(void) | ||
776 | { | ||
777 | unsigned long flags; | ||
778 | int ret; | ||
779 | |||
780 | ret = gsmi_system_valid(); | ||
781 | if (ret) | ||
782 | return ret; | ||
783 | |||
784 | gsmi_dev.smi_cmd = acpi_gbl_FADT.smi_command; | ||
785 | |||
786 | /* register device */ | ||
787 | gsmi_dev.pdev = platform_device_register_simple("gsmi", -1, NULL, 0); | ||
788 | if (IS_ERR(gsmi_dev.pdev)) { | ||
789 | printk(KERN_ERR "gsmi: unable to register platform device\n"); | ||
790 | return PTR_ERR(gsmi_dev.pdev); | ||
791 | } | ||
792 | |||
793 | /* SMI access needs to be serialized */ | ||
794 | spin_lock_init(&gsmi_dev.lock); | ||
795 | |||
796 | /* SMI callbacks require 32bit addresses */ | ||
797 | gsmi_dev.pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); | ||
798 | gsmi_dev.pdev->dev.dma_mask = | ||
799 | &gsmi_dev.pdev->dev.coherent_dma_mask; | ||
800 | ret = -ENOMEM; | ||
801 | gsmi_dev.dma_pool = dma_pool_create("gsmi", &gsmi_dev.pdev->dev, | ||
802 | GSMI_BUF_SIZE, GSMI_BUF_ALIGN, 0); | ||
803 | if (!gsmi_dev.dma_pool) | ||
804 | goto out_err; | ||
805 | |||
806 | /* | ||
807 | * pre-allocate buffers because sometimes we are called when | ||
808 | * this is not feasible: oops, panic, die, mce, etc | ||
809 | */ | ||
810 | gsmi_dev.name_buf = gsmi_buf_alloc(); | ||
811 | if (!gsmi_dev.name_buf) { | ||
812 | printk(KERN_ERR "gsmi: failed to allocate name buffer\n"); | ||
813 | goto out_err; | ||
814 | } | ||
815 | |||
816 | gsmi_dev.data_buf = gsmi_buf_alloc(); | ||
817 | if (!gsmi_dev.data_buf) { | ||
818 | printk(KERN_ERR "gsmi: failed to allocate data buffer\n"); | ||
819 | goto out_err; | ||
820 | } | ||
821 | |||
822 | gsmi_dev.param_buf = gsmi_buf_alloc(); | ||
823 | if (!gsmi_dev.param_buf) { | ||
824 | printk(KERN_ERR "gsmi: failed to allocate param buffer\n"); | ||
825 | goto out_err; | ||
826 | } | ||
827 | |||
828 | /* | ||
829 | * Determine type of handshake used to serialize the SMI | ||
830 | * entry. See also gsmi_exec(). | ||
831 | * | ||
832 | * There's a "behavior" present on some chipsets where writing the | ||
833 | * SMI trigger register in the southbridge doesn't result in an | ||
834 | * immediate SMI. Rather, the processor can execute "a few" more | ||
835 | * instructions before the SMI takes effect. To ensure synchronous | ||
836 | * behavior, implement a handshake between the kernel driver and the | ||
837 | * firmware handler to spin until released. This ioctl determines | ||
838 | * the type of handshake. | ||
839 | * | ||
840 | * NONE: The firmware handler does not implement any | ||
841 | * handshake. Either it doesn't need to, or it's legacy firmware | ||
842 | * that doesn't know it needs to and never will. | ||
843 | * | ||
844 | * CF: The firmware handler will clear the CF in the saved | ||
845 | * state before returning. The driver may set the CF and test for | ||
846 | * it to clear before proceeding. | ||
847 | * | ||
848 | * SPIN: The firmware handler does not implement any handshake | ||
849 | * but the driver should spin for a hundred or so microseconds | ||
850 | * to ensure the SMI has triggered. | ||
851 | * | ||
852 | * Finally, the handler will return -ENOSYS if | ||
853 | * GSMI_CMD_HANDSHAKE_TYPE is unimplemented, which implies | ||
854 | * HANDSHAKE_NONE. | ||
855 | */ | ||
856 | spin_lock_irqsave(&gsmi_dev.lock, flags); | ||
857 | gsmi_dev.handshake_type = GSMI_HANDSHAKE_SPIN; | ||
858 | gsmi_dev.handshake_type = | ||
859 | gsmi_exec(GSMI_CALLBACK, GSMI_CMD_HANDSHAKE_TYPE); | ||
860 | if (gsmi_dev.handshake_type == -ENOSYS) | ||
861 | gsmi_dev.handshake_type = GSMI_HANDSHAKE_NONE; | ||
862 | spin_unlock_irqrestore(&gsmi_dev.lock, flags); | ||
863 | |||
864 | /* Remove and clean up gsmi if the handshake could not complete. */ | ||
865 | if (gsmi_dev.handshake_type == -ENXIO) { | ||
866 | printk(KERN_INFO "gsmi version " DRIVER_VERSION | ||
867 | " failed to load\n"); | ||
868 | ret = -ENODEV; | ||
869 | goto out_err; | ||
870 | } | ||
871 | |||
872 | printk(KERN_INFO "gsmi version " DRIVER_VERSION " loaded\n"); | ||
873 | |||
874 | /* Register in the firmware directory */ | ||
875 | ret = -ENOMEM; | ||
876 | gsmi_kobj = kobject_create_and_add("gsmi", firmware_kobj); | ||
877 | if (!gsmi_kobj) { | ||
878 | printk(KERN_INFO "gsmi: Failed to create firmware kobj\n"); | ||
879 | goto out_err; | ||
880 | } | ||
881 | |||
882 | /* Setup eventlog access */ | ||
883 | ret = sysfs_create_bin_file(gsmi_kobj, &eventlog_bin_attr); | ||
884 | if (ret) { | ||
885 | printk(KERN_INFO "gsmi: Failed to setup eventlog"); | ||
886 | goto out_err; | ||
887 | } | ||
888 | |||
889 | /* Other attributes */ | ||
890 | ret = sysfs_create_files(gsmi_kobj, gsmi_attrs); | ||
891 | if (ret) { | ||
892 | printk(KERN_INFO "gsmi: Failed to add attrs"); | ||
893 | goto out_err; | ||
894 | } | ||
895 | |||
896 | if (register_efivars(&efivars, &efivar_ops, gsmi_kobj)) { | ||
897 | printk(KERN_INFO "gsmi: Failed to register efivars\n"); | ||
898 | goto out_err; | ||
899 | } | ||
900 | |||
901 | register_reboot_notifier(&gsmi_reboot_notifier); | ||
902 | register_die_notifier(&gsmi_die_notifier); | ||
903 | atomic_notifier_chain_register(&panic_notifier_list, | ||
904 | &gsmi_panic_notifier); | ||
905 | |||
906 | return 0; | ||
907 | |||
908 | out_err: | ||
909 | kobject_put(gsmi_kobj); | ||
910 | gsmi_buf_free(gsmi_dev.param_buf); | ||
911 | gsmi_buf_free(gsmi_dev.data_buf); | ||
912 | gsmi_buf_free(gsmi_dev.name_buf); | ||
913 | if (gsmi_dev.dma_pool) | ||
914 | dma_pool_destroy(gsmi_dev.dma_pool); | ||
915 | platform_device_unregister(gsmi_dev.pdev); | ||
916 | pr_info("gsmi: failed to load: %d\n", ret); | ||
917 | return ret; | ||
918 | } | ||
919 | |||
920 | static void __exit gsmi_exit(void) | ||
921 | { | ||
922 | unregister_reboot_notifier(&gsmi_reboot_notifier); | ||
923 | unregister_die_notifier(&gsmi_die_notifier); | ||
924 | atomic_notifier_chain_unregister(&panic_notifier_list, | ||
925 | &gsmi_panic_notifier); | ||
926 | unregister_efivars(&efivars); | ||
927 | |||
928 | kobject_put(gsmi_kobj); | ||
929 | gsmi_buf_free(gsmi_dev.param_buf); | ||
930 | gsmi_buf_free(gsmi_dev.data_buf); | ||
931 | gsmi_buf_free(gsmi_dev.name_buf); | ||
932 | dma_pool_destroy(gsmi_dev.dma_pool); | ||
933 | platform_device_unregister(gsmi_dev.pdev); | ||
934 | } | ||
935 | |||
936 | module_init(gsmi_init); | ||
937 | module_exit(gsmi_exit); | ||
938 | |||
939 | MODULE_AUTHOR("Google, Inc."); | ||
940 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/firmware/google/memconsole.c b/drivers/firmware/google/memconsole.c new file mode 100644 index 000000000000..2a90ba613613 --- /dev/null +++ b/drivers/firmware/google/memconsole.c | |||
@@ -0,0 +1,166 @@ | |||
1 | /* | ||
2 | * memconsole.c | ||
3 | * | ||
4 | * Infrastructure for importing the BIOS memory based console | ||
5 | * into the kernel log ringbuffer. | ||
6 | * | ||
7 | * Copyright 2010 Google Inc. All rights reserved. | ||
8 | */ | ||
9 | |||
10 | #include <linux/ctype.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/string.h> | ||
14 | #include <linux/sysfs.h> | ||
15 | #include <linux/kobject.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/dmi.h> | ||
18 | #include <asm/bios_ebda.h> | ||
19 | |||
20 | #define BIOS_MEMCONSOLE_V1_MAGIC 0xDEADBABE | ||
21 | #define BIOS_MEMCONSOLE_V2_MAGIC (('M')|('C'<<8)|('O'<<16)|('N'<<24)) | ||
22 | |||
23 | struct biosmemcon_ebda { | ||
24 | u32 signature; | ||
25 | union { | ||
26 | struct { | ||
27 | u8 enabled; | ||
28 | u32 buffer_addr; | ||
29 | u16 start; | ||
30 | u16 end; | ||
31 | u16 num_chars; | ||
32 | u8 wrapped; | ||
33 | } __packed v1; | ||
34 | struct { | ||
35 | u32 buffer_addr; | ||
36 | /* Misdocumented as number of pages! */ | ||
37 | u16 num_bytes; | ||
38 | u16 start; | ||
39 | u16 end; | ||
40 | } __packed v2; | ||
41 | }; | ||
42 | } __packed; | ||
43 | |||
44 | static char *memconsole_baseaddr; | ||
45 | static size_t memconsole_length; | ||
46 | |||
47 | static ssize_t memconsole_read(struct file *filp, struct kobject *kobp, | ||
48 | struct bin_attribute *bin_attr, char *buf, | ||
49 | loff_t pos, size_t count) | ||
50 | { | ||
51 | return memory_read_from_buffer(buf, count, &pos, memconsole_baseaddr, | ||
52 | memconsole_length); | ||
53 | } | ||
54 | |||
55 | static struct bin_attribute memconsole_bin_attr = { | ||
56 | .attr = {.name = "log", .mode = 0444}, | ||
57 | .read = memconsole_read, | ||
58 | }; | ||
59 | |||
60 | |||
61 | static void found_v1_header(struct biosmemcon_ebda *hdr) | ||
62 | { | ||
63 | printk(KERN_INFO "BIOS console v1 EBDA structure found at %p\n", hdr); | ||
64 | printk(KERN_INFO "BIOS console buffer at 0x%.8x, " | ||
65 | "start = %d, end = %d, num = %d\n", | ||
66 | hdr->v1.buffer_addr, hdr->v1.start, | ||
67 | hdr->v1.end, hdr->v1.num_chars); | ||
68 | |||
69 | memconsole_length = hdr->v1.num_chars; | ||
70 | memconsole_baseaddr = phys_to_virt(hdr->v1.buffer_addr); | ||
71 | } | ||
72 | |||
73 | static void found_v2_header(struct biosmemcon_ebda *hdr) | ||
74 | { | ||
75 | printk(KERN_INFO "BIOS console v2 EBDA structure found at %p\n", hdr); | ||
76 | printk(KERN_INFO "BIOS console buffer at 0x%.8x, " | ||
77 | "start = %d, end = %d, num_bytes = %d\n", | ||
78 | hdr->v2.buffer_addr, hdr->v2.start, | ||
79 | hdr->v2.end, hdr->v2.num_bytes); | ||
80 | |||
81 | memconsole_length = hdr->v2.end - hdr->v2.start; | ||
82 | memconsole_baseaddr = phys_to_virt(hdr->v2.buffer_addr | ||
83 | + hdr->v2.start); | ||
84 | } | ||
85 | |||
86 | /* | ||
87 | * Search through the EBDA for the BIOS Memory Console, and | ||
88 | * set the global variables to point to it. Return true if found. | ||
89 | */ | ||
90 | static bool found_memconsole(void) | ||
91 | { | ||
92 | unsigned int address; | ||
93 | size_t length, cur; | ||
94 | |||
95 | address = get_bios_ebda(); | ||
96 | if (!address) { | ||
97 | printk(KERN_INFO "BIOS EBDA non-existent.\n"); | ||
98 | return false; | ||
99 | } | ||
100 | |||
101 | /* EBDA length is byte 0 of EBDA (in KB) */ | ||
102 | length = *(u8 *)phys_to_virt(address); | ||
103 | length <<= 10; /* convert to bytes */ | ||
104 | |||
105 | /* | ||
106 | * Search through EBDA for BIOS memory console structure | ||
107 | * note: signature is not necessarily dword-aligned | ||
108 | */ | ||
109 | for (cur = 0; cur < length; cur++) { | ||
110 | struct biosmemcon_ebda *hdr = phys_to_virt(address + cur); | ||
111 | |||
112 | /* memconsole v1 */ | ||
113 | if (hdr->signature == BIOS_MEMCONSOLE_V1_MAGIC) { | ||
114 | found_v1_header(hdr); | ||
115 | return true; | ||
116 | } | ||
117 | |||
118 | /* memconsole v2 */ | ||
119 | if (hdr->signature == BIOS_MEMCONSOLE_V2_MAGIC) { | ||
120 | found_v2_header(hdr); | ||
121 | return true; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | printk(KERN_INFO "BIOS console EBDA structure not found!\n"); | ||
126 | return false; | ||
127 | } | ||
128 | |||
129 | static struct dmi_system_id memconsole_dmi_table[] __initdata = { | ||
130 | { | ||
131 | .ident = "Google Board", | ||
132 | .matches = { | ||
133 | DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."), | ||
134 | }, | ||
135 | }, | ||
136 | {} | ||
137 | }; | ||
138 | MODULE_DEVICE_TABLE(dmi, memconsole_dmi_table); | ||
139 | |||
140 | static int __init memconsole_init(void) | ||
141 | { | ||
142 | int ret; | ||
143 | |||
144 | if (!dmi_check_system(memconsole_dmi_table)) | ||
145 | return -ENODEV; | ||
146 | |||
147 | if (!found_memconsole()) | ||
148 | return -ENODEV; | ||
149 | |||
150 | memconsole_bin_attr.size = memconsole_length; | ||
151 | |||
152 | ret = sysfs_create_bin_file(firmware_kobj, &memconsole_bin_attr); | ||
153 | |||
154 | return ret; | ||
155 | } | ||
156 | |||
157 | static void __exit memconsole_exit(void) | ||
158 | { | ||
159 | sysfs_remove_bin_file(firmware_kobj, &memconsole_bin_attr); | ||
160 | } | ||
161 | |||
162 | module_init(memconsole_init); | ||
163 | module_exit(memconsole_exit); | ||
164 | |||
165 | MODULE_AUTHOR("Google, Inc."); | ||
166 | MODULE_LICENSE("GPL"); | ||