| Mike Waychison | 74c5b31 | 2011-04-29 17:39:19 -0700 | [diff] [blame] | 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> | 
| Paul Gortmaker | afd605f | 2011-07-03 13:38:03 -0400 | [diff] [blame] | 30 | #include <linux/module.h> | 
| Mike Waychison | 74c5b31 | 2011-04-29 17:39:19 -0700 | [diff] [blame] | 31 |  | 
|  | 32 | #define GSMI_SHUTDOWN_CLEAN	0	/* Clean Shutdown */ | 
|  | 33 | /* TODO(mikew@google.com): Tie in HARDLOCKUP_DETECTOR with NMIWDT */ | 
|  | 34 | #define GSMI_SHUTDOWN_NMIWDT	1	/* NMI Watchdog */ | 
|  | 35 | #define GSMI_SHUTDOWN_PANIC	2	/* Panic */ | 
|  | 36 | #define GSMI_SHUTDOWN_OOPS	3	/* Oops */ | 
|  | 37 | #define GSMI_SHUTDOWN_DIE	4	/* Die -- No longer meaningful */ | 
|  | 38 | #define GSMI_SHUTDOWN_MCE	5	/* Machine Check */ | 
|  | 39 | #define GSMI_SHUTDOWN_SOFTWDT	6	/* Software Watchdog */ | 
|  | 40 | #define GSMI_SHUTDOWN_MBE	7	/* Uncorrected ECC */ | 
|  | 41 | #define GSMI_SHUTDOWN_TRIPLE	8	/* Triple Fault */ | 
|  | 42 |  | 
|  | 43 | #define DRIVER_VERSION		"1.0" | 
|  | 44 | #define GSMI_GUID_SIZE		16 | 
|  | 45 | #define GSMI_BUF_SIZE		1024 | 
|  | 46 | #define GSMI_BUF_ALIGN		sizeof(u64) | 
|  | 47 | #define GSMI_CALLBACK		0xef | 
|  | 48 |  | 
|  | 49 | /* SMI return codes */ | 
|  | 50 | #define GSMI_SUCCESS		0x00 | 
|  | 51 | #define GSMI_UNSUPPORTED2	0x03 | 
|  | 52 | #define GSMI_LOG_FULL		0x0b | 
|  | 53 | #define GSMI_VAR_NOT_FOUND	0x0e | 
|  | 54 | #define GSMI_HANDSHAKE_SPIN	0x7d | 
|  | 55 | #define GSMI_HANDSHAKE_CF	0x7e | 
|  | 56 | #define GSMI_HANDSHAKE_NONE	0x7f | 
|  | 57 | #define GSMI_INVALID_PARAMETER	0x82 | 
|  | 58 | #define GSMI_UNSUPPORTED	0x83 | 
|  | 59 | #define GSMI_BUFFER_TOO_SMALL	0x85 | 
|  | 60 | #define GSMI_NOT_READY		0x86 | 
|  | 61 | #define GSMI_DEVICE_ERROR	0x87 | 
|  | 62 | #define GSMI_NOT_FOUND		0x8e | 
|  | 63 |  | 
|  | 64 | #define QUIRKY_BOARD_HASH 0x78a30a50 | 
|  | 65 |  | 
|  | 66 | /* Internally used commands passed to the firmware */ | 
|  | 67 | #define GSMI_CMD_GET_NVRAM_VAR		0x01 | 
|  | 68 | #define GSMI_CMD_GET_NEXT_VAR		0x02 | 
|  | 69 | #define GSMI_CMD_SET_NVRAM_VAR		0x03 | 
|  | 70 | #define GSMI_CMD_SET_EVENT_LOG		0x08 | 
|  | 71 | #define GSMI_CMD_CLEAR_EVENT_LOG	0x09 | 
|  | 72 | #define GSMI_CMD_CLEAR_CONFIG		0x20 | 
|  | 73 | #define GSMI_CMD_HANDSHAKE_TYPE		0xC1 | 
|  | 74 |  | 
|  | 75 | /* Magic entry type for kernel events */ | 
|  | 76 | #define GSMI_LOG_ENTRY_TYPE_KERNEL     0xDEAD | 
|  | 77 |  | 
|  | 78 | /* SMI buffers must be in 32bit physical address space */ | 
|  | 79 | struct gsmi_buf { | 
|  | 80 | u8 *start;			/* start of buffer */ | 
|  | 81 | size_t length;			/* length of buffer */ | 
|  | 82 | dma_addr_t handle;		/* dma allocation handle */ | 
|  | 83 | u32 address;			/* physical address of buffer */ | 
|  | 84 | }; | 
|  | 85 |  | 
|  | 86 | struct gsmi_device { | 
|  | 87 | struct platform_device *pdev;	/* platform device */ | 
|  | 88 | struct gsmi_buf *name_buf;	/* variable name buffer */ | 
|  | 89 | struct gsmi_buf *data_buf;	/* generic data buffer */ | 
|  | 90 | struct gsmi_buf *param_buf;	/* parameter buffer */ | 
|  | 91 | spinlock_t lock;		/* serialize access to SMIs */ | 
|  | 92 | u16 smi_cmd;			/* SMI command port */ | 
|  | 93 | int handshake_type;		/* firmware handler interlock type */ | 
|  | 94 | struct dma_pool *dma_pool;	/* DMA buffer pool */ | 
|  | 95 | } gsmi_dev; | 
|  | 96 |  | 
|  | 97 | /* Packed structures for communicating with the firmware */ | 
|  | 98 | struct gsmi_nvram_var_param { | 
|  | 99 | efi_guid_t	guid; | 
|  | 100 | u32		name_ptr; | 
|  | 101 | u32		attributes; | 
|  | 102 | u32		data_len; | 
|  | 103 | u32		data_ptr; | 
|  | 104 | } __packed; | 
|  | 105 |  | 
|  | 106 | struct gsmi_get_next_var_param { | 
|  | 107 | u8	guid[GSMI_GUID_SIZE]; | 
|  | 108 | u32	name_ptr; | 
|  | 109 | u32	name_len; | 
|  | 110 | } __packed; | 
|  | 111 |  | 
|  | 112 | struct gsmi_set_eventlog_param { | 
|  | 113 | u32	data_ptr; | 
|  | 114 | u32	data_len; | 
|  | 115 | u32	type; | 
|  | 116 | } __packed; | 
|  | 117 |  | 
|  | 118 | /* Event log formats */ | 
|  | 119 | struct gsmi_log_entry_type_1 { | 
|  | 120 | u16	type; | 
|  | 121 | u32	instance; | 
|  | 122 | } __packed; | 
|  | 123 |  | 
|  | 124 |  | 
|  | 125 | /* | 
|  | 126 | * Some platforms don't have explicit SMI handshake | 
|  | 127 | * and need to wait for SMI to complete. | 
|  | 128 | */ | 
|  | 129 | #define GSMI_DEFAULT_SPINCOUNT	0x10000 | 
|  | 130 | static unsigned int spincount = GSMI_DEFAULT_SPINCOUNT; | 
|  | 131 | module_param(spincount, uint, 0600); | 
|  | 132 | MODULE_PARM_DESC(spincount, | 
|  | 133 | "The number of loop iterations to use when using the spin handshake."); | 
|  | 134 |  | 
|  | 135 | static struct gsmi_buf *gsmi_buf_alloc(void) | 
|  | 136 | { | 
|  | 137 | struct gsmi_buf *smibuf; | 
|  | 138 |  | 
|  | 139 | smibuf = kzalloc(sizeof(*smibuf), GFP_KERNEL); | 
|  | 140 | if (!smibuf) { | 
|  | 141 | printk(KERN_ERR "gsmi: out of memory\n"); | 
|  | 142 | return NULL; | 
|  | 143 | } | 
|  | 144 |  | 
|  | 145 | /* allocate buffer in 32bit address space */ | 
|  | 146 | smibuf->start = dma_pool_alloc(gsmi_dev.dma_pool, GFP_KERNEL, | 
|  | 147 | &smibuf->handle); | 
|  | 148 | if (!smibuf->start) { | 
|  | 149 | printk(KERN_ERR "gsmi: failed to allocate name buffer\n"); | 
|  | 150 | kfree(smibuf); | 
|  | 151 | return NULL; | 
|  | 152 | } | 
|  | 153 |  | 
|  | 154 | /* fill in the buffer handle */ | 
|  | 155 | smibuf->length = GSMI_BUF_SIZE; | 
|  | 156 | smibuf->address = (u32)virt_to_phys(smibuf->start); | 
|  | 157 |  | 
|  | 158 | return smibuf; | 
|  | 159 | } | 
|  | 160 |  | 
|  | 161 | static void gsmi_buf_free(struct gsmi_buf *smibuf) | 
|  | 162 | { | 
|  | 163 | if (smibuf) { | 
|  | 164 | if (smibuf->start) | 
|  | 165 | dma_pool_free(gsmi_dev.dma_pool, smibuf->start, | 
|  | 166 | smibuf->handle); | 
|  | 167 | kfree(smibuf); | 
|  | 168 | } | 
|  | 169 | } | 
|  | 170 |  | 
|  | 171 | /* | 
|  | 172 | * Make a call to gsmi func(sub).  GSMI error codes are translated to | 
|  | 173 | * in-kernel errnos (0 on success, -ERRNO on error). | 
|  | 174 | */ | 
|  | 175 | static int gsmi_exec(u8 func, u8 sub) | 
|  | 176 | { | 
|  | 177 | u16 cmd = (sub << 8) | func; | 
|  | 178 | u16 result = 0; | 
|  | 179 | int rc = 0; | 
|  | 180 |  | 
|  | 181 | /* | 
|  | 182 | * AH  : Subfunction number | 
|  | 183 | * AL  : Function number | 
|  | 184 | * EBX : Parameter block address | 
|  | 185 | * DX  : SMI command port | 
|  | 186 | * | 
|  | 187 | * Three protocols here. See also the comment in gsmi_init(). | 
|  | 188 | */ | 
|  | 189 | if (gsmi_dev.handshake_type == GSMI_HANDSHAKE_CF) { | 
|  | 190 | /* | 
|  | 191 | * If handshake_type == HANDSHAKE_CF then set CF on the | 
|  | 192 | * way in and wait for the handler to clear it; this avoids | 
|  | 193 | * corrupting register state on those chipsets which have | 
|  | 194 | * a delay between writing the SMI trigger register and | 
|  | 195 | * entering SMM. | 
|  | 196 | */ | 
|  | 197 | asm volatile ( | 
|  | 198 | "stc\n" | 
|  | 199 | "outb %%al, %%dx\n" | 
|  | 200 | "1:      jc 1b\n" | 
|  | 201 | : "=a" (result) | 
|  | 202 | : "0" (cmd), | 
|  | 203 | "d" (gsmi_dev.smi_cmd), | 
|  | 204 | "b" (gsmi_dev.param_buf->address) | 
|  | 205 | : "memory", "cc" | 
|  | 206 | ); | 
|  | 207 | } else if (gsmi_dev.handshake_type == GSMI_HANDSHAKE_SPIN) { | 
|  | 208 | /* | 
|  | 209 | * If handshake_type == HANDSHAKE_SPIN we spin a | 
|  | 210 | * hundred-ish usecs to ensure the SMI has triggered. | 
|  | 211 | */ | 
|  | 212 | asm volatile ( | 
|  | 213 | "outb %%al, %%dx\n" | 
|  | 214 | "1:      loop 1b\n" | 
|  | 215 | : "=a" (result) | 
|  | 216 | : "0" (cmd), | 
|  | 217 | "d" (gsmi_dev.smi_cmd), | 
|  | 218 | "b" (gsmi_dev.param_buf->address), | 
|  | 219 | "c" (spincount) | 
|  | 220 | : "memory", "cc" | 
|  | 221 | ); | 
|  | 222 | } else { | 
|  | 223 | /* | 
|  | 224 | * If handshake_type == HANDSHAKE_NONE we do nothing; | 
|  | 225 | * either we don't need to or it's legacy firmware that | 
|  | 226 | * doesn't understand the CF protocol. | 
|  | 227 | */ | 
|  | 228 | asm volatile ( | 
|  | 229 | "outb %%al, %%dx\n\t" | 
|  | 230 | : "=a" (result) | 
|  | 231 | : "0" (cmd), | 
|  | 232 | "d" (gsmi_dev.smi_cmd), | 
|  | 233 | "b" (gsmi_dev.param_buf->address) | 
|  | 234 | : "memory", "cc" | 
|  | 235 | ); | 
|  | 236 | } | 
|  | 237 |  | 
|  | 238 | /* check return code from SMI handler */ | 
|  | 239 | switch (result) { | 
|  | 240 | case GSMI_SUCCESS: | 
|  | 241 | break; | 
|  | 242 | case GSMI_VAR_NOT_FOUND: | 
|  | 243 | /* not really an error, but let the caller know */ | 
|  | 244 | rc = 1; | 
|  | 245 | break; | 
|  | 246 | case GSMI_INVALID_PARAMETER: | 
|  | 247 | printk(KERN_ERR "gsmi: exec 0x%04x: Invalid parameter\n", cmd); | 
|  | 248 | rc = -EINVAL; | 
|  | 249 | break; | 
|  | 250 | case GSMI_BUFFER_TOO_SMALL: | 
|  | 251 | printk(KERN_ERR "gsmi: exec 0x%04x: Buffer too small\n", cmd); | 
|  | 252 | rc = -ENOMEM; | 
|  | 253 | break; | 
|  | 254 | case GSMI_UNSUPPORTED: | 
|  | 255 | case GSMI_UNSUPPORTED2: | 
|  | 256 | if (sub != GSMI_CMD_HANDSHAKE_TYPE) | 
|  | 257 | printk(KERN_ERR "gsmi: exec 0x%04x: Not supported\n", | 
|  | 258 | cmd); | 
|  | 259 | rc = -ENOSYS; | 
|  | 260 | break; | 
|  | 261 | case GSMI_NOT_READY: | 
|  | 262 | printk(KERN_ERR "gsmi: exec 0x%04x: Not ready\n", cmd); | 
|  | 263 | rc = -EBUSY; | 
|  | 264 | break; | 
|  | 265 | case GSMI_DEVICE_ERROR: | 
|  | 266 | printk(KERN_ERR "gsmi: exec 0x%04x: Device error\n", cmd); | 
|  | 267 | rc = -EFAULT; | 
|  | 268 | break; | 
|  | 269 | case GSMI_NOT_FOUND: | 
|  | 270 | printk(KERN_ERR "gsmi: exec 0x%04x: Data not found\n", cmd); | 
|  | 271 | rc = -ENOENT; | 
|  | 272 | break; | 
|  | 273 | case GSMI_LOG_FULL: | 
|  | 274 | printk(KERN_ERR "gsmi: exec 0x%04x: Log full\n", cmd); | 
|  | 275 | rc = -ENOSPC; | 
|  | 276 | break; | 
|  | 277 | case GSMI_HANDSHAKE_CF: | 
|  | 278 | case GSMI_HANDSHAKE_SPIN: | 
|  | 279 | case GSMI_HANDSHAKE_NONE: | 
|  | 280 | rc = result; | 
|  | 281 | break; | 
|  | 282 | default: | 
|  | 283 | printk(KERN_ERR "gsmi: exec 0x%04x: Unknown error 0x%04x\n", | 
|  | 284 | cmd, result); | 
|  | 285 | rc = -ENXIO; | 
|  | 286 | } | 
|  | 287 |  | 
|  | 288 | return rc; | 
|  | 289 | } | 
|  | 290 |  | 
|  | 291 | /* Return the number of unicode characters in data */ | 
|  | 292 | static size_t | 
|  | 293 | utf16_strlen(efi_char16_t *data, unsigned long maxlength) | 
|  | 294 | { | 
|  | 295 | unsigned long length = 0; | 
|  | 296 |  | 
|  | 297 | while (*data++ != 0 && length < maxlength) | 
|  | 298 | length++; | 
|  | 299 | return length; | 
|  | 300 | } | 
|  | 301 |  | 
|  | 302 | static efi_status_t gsmi_get_variable(efi_char16_t *name, | 
|  | 303 | efi_guid_t *vendor, u32 *attr, | 
|  | 304 | unsigned long *data_size, | 
|  | 305 | void *data) | 
|  | 306 | { | 
|  | 307 | struct gsmi_nvram_var_param param = { | 
|  | 308 | .name_ptr = gsmi_dev.name_buf->address, | 
|  | 309 | .data_ptr = gsmi_dev.data_buf->address, | 
|  | 310 | .data_len = (u32)*data_size, | 
|  | 311 | }; | 
|  | 312 | efi_status_t ret = EFI_SUCCESS; | 
|  | 313 | unsigned long flags; | 
|  | 314 | size_t name_len = utf16_strlen(name, GSMI_BUF_SIZE / 2); | 
|  | 315 | int rc; | 
|  | 316 |  | 
|  | 317 | if (name_len >= GSMI_BUF_SIZE / 2) | 
|  | 318 | return EFI_BAD_BUFFER_SIZE; | 
|  | 319 |  | 
|  | 320 | spin_lock_irqsave(&gsmi_dev.lock, flags); | 
|  | 321 |  | 
|  | 322 | /* Vendor guid */ | 
|  | 323 | memcpy(¶m.guid, vendor, sizeof(param.guid)); | 
|  | 324 |  | 
|  | 325 | /* variable name, already in UTF-16 */ | 
|  | 326 | memset(gsmi_dev.name_buf->start, 0, gsmi_dev.name_buf->length); | 
|  | 327 | memcpy(gsmi_dev.name_buf->start, name, name_len * 2); | 
|  | 328 |  | 
|  | 329 | /* data pointer */ | 
|  | 330 | memset(gsmi_dev.data_buf->start, 0, gsmi_dev.data_buf->length); | 
|  | 331 |  | 
|  | 332 | /* parameter buffer */ | 
|  | 333 | memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length); | 
|  | 334 | memcpy(gsmi_dev.param_buf->start, ¶m, sizeof(param)); | 
|  | 335 |  | 
|  | 336 | rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_GET_NVRAM_VAR); | 
|  | 337 | if (rc < 0) { | 
|  | 338 | printk(KERN_ERR "gsmi: Get Variable failed\n"); | 
|  | 339 | ret = EFI_LOAD_ERROR; | 
|  | 340 | } else if (rc == 1) { | 
|  | 341 | /* variable was not found */ | 
|  | 342 | ret = EFI_NOT_FOUND; | 
|  | 343 | } else { | 
|  | 344 | /* Get the arguments back */ | 
|  | 345 | memcpy(¶m, gsmi_dev.param_buf->start, sizeof(param)); | 
|  | 346 |  | 
|  | 347 | /* The size reported is the min of all of our buffers */ | 
| Maxin B. John | d2554f5 | 2011-12-01 01:37:33 +0200 | [diff] [blame] | 348 | *data_size = min_t(unsigned long, *data_size, | 
|  | 349 | gsmi_dev.data_buf->length); | 
| Mike Waychison | 74c5b31 | 2011-04-29 17:39:19 -0700 | [diff] [blame] | 350 | *data_size = min_t(unsigned long, *data_size, param.data_len); | 
|  | 351 |  | 
|  | 352 | /* Copy data back to return buffer. */ | 
|  | 353 | memcpy(data, gsmi_dev.data_buf->start, *data_size); | 
|  | 354 |  | 
|  | 355 | /* All variables are have the following attributes */ | 
|  | 356 | *attr = EFI_VARIABLE_NON_VOLATILE | | 
|  | 357 | EFI_VARIABLE_BOOTSERVICE_ACCESS | | 
|  | 358 | EFI_VARIABLE_RUNTIME_ACCESS; | 
|  | 359 | } | 
|  | 360 |  | 
|  | 361 | spin_unlock_irqrestore(&gsmi_dev.lock, flags); | 
|  | 362 |  | 
|  | 363 | return ret; | 
|  | 364 | } | 
|  | 365 |  | 
|  | 366 | static efi_status_t gsmi_get_next_variable(unsigned long *name_size, | 
|  | 367 | efi_char16_t *name, | 
|  | 368 | efi_guid_t *vendor) | 
|  | 369 | { | 
|  | 370 | struct gsmi_get_next_var_param param = { | 
|  | 371 | .name_ptr = gsmi_dev.name_buf->address, | 
|  | 372 | .name_len = gsmi_dev.name_buf->length, | 
|  | 373 | }; | 
|  | 374 | efi_status_t ret = EFI_SUCCESS; | 
|  | 375 | int rc; | 
|  | 376 | unsigned long flags; | 
|  | 377 |  | 
|  | 378 | /* For the moment, only support buffers that exactly match in size */ | 
|  | 379 | if (*name_size != GSMI_BUF_SIZE) | 
|  | 380 | return EFI_BAD_BUFFER_SIZE; | 
|  | 381 |  | 
|  | 382 | /* Let's make sure the thing is at least null-terminated */ | 
|  | 383 | if (utf16_strlen(name, GSMI_BUF_SIZE / 2) == GSMI_BUF_SIZE / 2) | 
|  | 384 | return EFI_INVALID_PARAMETER; | 
|  | 385 |  | 
|  | 386 | spin_lock_irqsave(&gsmi_dev.lock, flags); | 
|  | 387 |  | 
|  | 388 | /* guid */ | 
|  | 389 | memcpy(¶m.guid, vendor, sizeof(param.guid)); | 
|  | 390 |  | 
|  | 391 | /* variable name, already in UTF-16 */ | 
|  | 392 | memcpy(gsmi_dev.name_buf->start, name, *name_size); | 
|  | 393 |  | 
|  | 394 | /* parameter buffer */ | 
|  | 395 | memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length); | 
|  | 396 | memcpy(gsmi_dev.param_buf->start, ¶m, sizeof(param)); | 
|  | 397 |  | 
|  | 398 | rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_GET_NEXT_VAR); | 
|  | 399 | if (rc < 0) { | 
|  | 400 | printk(KERN_ERR "gsmi: Get Next Variable Name failed\n"); | 
|  | 401 | ret = EFI_LOAD_ERROR; | 
|  | 402 | } else if (rc == 1) { | 
|  | 403 | /* variable not found -- end of list */ | 
|  | 404 | ret = EFI_NOT_FOUND; | 
|  | 405 | } else { | 
|  | 406 | /* copy variable data back to return buffer */ | 
|  | 407 | memcpy(¶m, gsmi_dev.param_buf->start, sizeof(param)); | 
|  | 408 |  | 
|  | 409 | /* Copy the name back */ | 
|  | 410 | memcpy(name, gsmi_dev.name_buf->start, GSMI_BUF_SIZE); | 
|  | 411 | *name_size = utf16_strlen(name, GSMI_BUF_SIZE / 2) * 2; | 
|  | 412 |  | 
|  | 413 | /* copy guid to return buffer */ | 
|  | 414 | memcpy(vendor, ¶m.guid, sizeof(param.guid)); | 
|  | 415 | ret = EFI_SUCCESS; | 
|  | 416 | } | 
|  | 417 |  | 
|  | 418 | spin_unlock_irqrestore(&gsmi_dev.lock, flags); | 
|  | 419 |  | 
|  | 420 | return ret; | 
|  | 421 | } | 
|  | 422 |  | 
|  | 423 | static efi_status_t gsmi_set_variable(efi_char16_t *name, | 
|  | 424 | efi_guid_t *vendor, | 
| Randy Dunlap | a88769c | 2011-07-27 10:11:28 -0700 | [diff] [blame] | 425 | u32 attr, | 
| Mike Waychison | 74c5b31 | 2011-04-29 17:39:19 -0700 | [diff] [blame] | 426 | unsigned long data_size, | 
|  | 427 | void *data) | 
|  | 428 | { | 
|  | 429 | struct gsmi_nvram_var_param param = { | 
|  | 430 | .name_ptr = gsmi_dev.name_buf->address, | 
|  | 431 | .data_ptr = gsmi_dev.data_buf->address, | 
|  | 432 | .data_len = (u32)data_size, | 
|  | 433 | .attributes = EFI_VARIABLE_NON_VOLATILE | | 
|  | 434 | EFI_VARIABLE_BOOTSERVICE_ACCESS | | 
|  | 435 | EFI_VARIABLE_RUNTIME_ACCESS, | 
|  | 436 | }; | 
|  | 437 | size_t name_len = utf16_strlen(name, GSMI_BUF_SIZE / 2); | 
|  | 438 | efi_status_t ret = EFI_SUCCESS; | 
|  | 439 | int rc; | 
|  | 440 | unsigned long flags; | 
|  | 441 |  | 
|  | 442 | if (name_len >= GSMI_BUF_SIZE / 2) | 
|  | 443 | return EFI_BAD_BUFFER_SIZE; | 
|  | 444 |  | 
|  | 445 | spin_lock_irqsave(&gsmi_dev.lock, flags); | 
|  | 446 |  | 
|  | 447 | /* guid */ | 
|  | 448 | memcpy(¶m.guid, vendor, sizeof(param.guid)); | 
|  | 449 |  | 
|  | 450 | /* variable name, already in UTF-16 */ | 
|  | 451 | memset(gsmi_dev.name_buf->start, 0, gsmi_dev.name_buf->length); | 
|  | 452 | memcpy(gsmi_dev.name_buf->start, name, name_len * 2); | 
|  | 453 |  | 
|  | 454 | /* data pointer */ | 
|  | 455 | memset(gsmi_dev.data_buf->start, 0, gsmi_dev.data_buf->length); | 
|  | 456 | memcpy(gsmi_dev.data_buf->start, data, data_size); | 
|  | 457 |  | 
|  | 458 | /* parameter buffer */ | 
|  | 459 | memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length); | 
|  | 460 | memcpy(gsmi_dev.param_buf->start, ¶m, sizeof(param)); | 
|  | 461 |  | 
|  | 462 | rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_SET_NVRAM_VAR); | 
|  | 463 | if (rc < 0) { | 
|  | 464 | printk(KERN_ERR "gsmi: Set Variable failed\n"); | 
|  | 465 | ret = EFI_INVALID_PARAMETER; | 
|  | 466 | } | 
|  | 467 |  | 
|  | 468 | spin_unlock_irqrestore(&gsmi_dev.lock, flags); | 
|  | 469 |  | 
|  | 470 | return ret; | 
|  | 471 | } | 
|  | 472 |  | 
|  | 473 | static const struct efivar_operations efivar_ops = { | 
|  | 474 | .get_variable = gsmi_get_variable, | 
|  | 475 | .set_variable = gsmi_set_variable, | 
|  | 476 | .get_next_variable = gsmi_get_next_variable, | 
|  | 477 | }; | 
|  | 478 |  | 
|  | 479 | static ssize_t eventlog_write(struct file *filp, struct kobject *kobj, | 
|  | 480 | struct bin_attribute *bin_attr, | 
|  | 481 | char *buf, loff_t pos, size_t count) | 
|  | 482 | { | 
|  | 483 | struct gsmi_set_eventlog_param param = { | 
|  | 484 | .data_ptr = gsmi_dev.data_buf->address, | 
|  | 485 | }; | 
|  | 486 | int rc = 0; | 
|  | 487 | unsigned long flags; | 
|  | 488 |  | 
|  | 489 | /* Pull the type out */ | 
|  | 490 | if (count < sizeof(u32)) | 
|  | 491 | return -EINVAL; | 
|  | 492 | param.type = *(u32 *)buf; | 
|  | 493 | count -= sizeof(u32); | 
|  | 494 | buf += sizeof(u32); | 
|  | 495 |  | 
|  | 496 | /* The remaining buffer is the data payload */ | 
|  | 497 | if (count > gsmi_dev.data_buf->length) | 
|  | 498 | return -EINVAL; | 
|  | 499 | param.data_len = count - sizeof(u32); | 
|  | 500 |  | 
|  | 501 | spin_lock_irqsave(&gsmi_dev.lock, flags); | 
|  | 502 |  | 
|  | 503 | /* data pointer */ | 
|  | 504 | memset(gsmi_dev.data_buf->start, 0, gsmi_dev.data_buf->length); | 
|  | 505 | memcpy(gsmi_dev.data_buf->start, buf, param.data_len); | 
|  | 506 |  | 
|  | 507 | /* parameter buffer */ | 
|  | 508 | memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length); | 
|  | 509 | memcpy(gsmi_dev.param_buf->start, ¶m, sizeof(param)); | 
|  | 510 |  | 
|  | 511 | rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_SET_EVENT_LOG); | 
|  | 512 | if (rc < 0) | 
|  | 513 | printk(KERN_ERR "gsmi: Set Event Log failed\n"); | 
|  | 514 |  | 
|  | 515 | spin_unlock_irqrestore(&gsmi_dev.lock, flags); | 
|  | 516 |  | 
|  | 517 | return rc; | 
|  | 518 |  | 
|  | 519 | } | 
|  | 520 |  | 
|  | 521 | static struct bin_attribute eventlog_bin_attr = { | 
|  | 522 | .attr = {.name = "append_to_eventlog", .mode = 0200}, | 
|  | 523 | .write = eventlog_write, | 
|  | 524 | }; | 
|  | 525 |  | 
|  | 526 | static ssize_t gsmi_clear_eventlog_store(struct kobject *kobj, | 
|  | 527 | struct kobj_attribute *attr, | 
|  | 528 | const char *buf, size_t count) | 
|  | 529 | { | 
|  | 530 | int rc; | 
|  | 531 | unsigned long flags; | 
|  | 532 | unsigned long val; | 
|  | 533 | struct { | 
|  | 534 | u32 percentage; | 
|  | 535 | u32 data_type; | 
|  | 536 | } param; | 
|  | 537 |  | 
|  | 538 | rc = strict_strtoul(buf, 0, &val); | 
|  | 539 | if (rc) | 
|  | 540 | return rc; | 
|  | 541 |  | 
|  | 542 | /* | 
|  | 543 | * Value entered is a percentage, 0 through 100, anything else | 
|  | 544 | * is invalid. | 
|  | 545 | */ | 
|  | 546 | if (val > 100) | 
|  | 547 | return -EINVAL; | 
|  | 548 |  | 
|  | 549 | /* data_type here selects the smbios event log. */ | 
|  | 550 | param.percentage = val; | 
|  | 551 | param.data_type = 0; | 
|  | 552 |  | 
|  | 553 | spin_lock_irqsave(&gsmi_dev.lock, flags); | 
|  | 554 |  | 
|  | 555 | /* parameter buffer */ | 
|  | 556 | memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length); | 
|  | 557 | memcpy(gsmi_dev.param_buf->start, ¶m, sizeof(param)); | 
|  | 558 |  | 
|  | 559 | rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_CLEAR_EVENT_LOG); | 
|  | 560 |  | 
|  | 561 | spin_unlock_irqrestore(&gsmi_dev.lock, flags); | 
|  | 562 |  | 
|  | 563 | if (rc) | 
|  | 564 | return rc; | 
|  | 565 | return count; | 
|  | 566 | } | 
|  | 567 |  | 
|  | 568 | static struct kobj_attribute gsmi_clear_eventlog_attr = { | 
|  | 569 | .attr = {.name = "clear_eventlog", .mode = 0200}, | 
|  | 570 | .store = gsmi_clear_eventlog_store, | 
|  | 571 | }; | 
|  | 572 |  | 
|  | 573 | static ssize_t gsmi_clear_config_store(struct kobject *kobj, | 
|  | 574 | struct kobj_attribute *attr, | 
|  | 575 | const char *buf, size_t count) | 
|  | 576 | { | 
|  | 577 | int rc; | 
|  | 578 | unsigned long flags; | 
|  | 579 |  | 
|  | 580 | spin_lock_irqsave(&gsmi_dev.lock, flags); | 
|  | 581 |  | 
|  | 582 | /* clear parameter buffer */ | 
|  | 583 | memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length); | 
|  | 584 |  | 
|  | 585 | rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_CLEAR_CONFIG); | 
|  | 586 |  | 
|  | 587 | spin_unlock_irqrestore(&gsmi_dev.lock, flags); | 
|  | 588 |  | 
|  | 589 | if (rc) | 
|  | 590 | return rc; | 
|  | 591 | return count; | 
|  | 592 | } | 
|  | 593 |  | 
|  | 594 | static struct kobj_attribute gsmi_clear_config_attr = { | 
|  | 595 | .attr = {.name = "clear_config", .mode = 0200}, | 
|  | 596 | .store = gsmi_clear_config_store, | 
|  | 597 | }; | 
|  | 598 |  | 
|  | 599 | static const struct attribute *gsmi_attrs[] = { | 
|  | 600 | &gsmi_clear_config_attr.attr, | 
|  | 601 | &gsmi_clear_eventlog_attr.attr, | 
|  | 602 | NULL, | 
|  | 603 | }; | 
|  | 604 |  | 
|  | 605 | static int gsmi_shutdown_reason(int reason) | 
|  | 606 | { | 
|  | 607 | struct gsmi_log_entry_type_1 entry = { | 
|  | 608 | .type     = GSMI_LOG_ENTRY_TYPE_KERNEL, | 
|  | 609 | .instance = reason, | 
|  | 610 | }; | 
|  | 611 | struct gsmi_set_eventlog_param param = { | 
|  | 612 | .data_len = sizeof(entry), | 
|  | 613 | .type     = 1, | 
|  | 614 | }; | 
|  | 615 | static int saved_reason; | 
|  | 616 | int rc = 0; | 
|  | 617 | unsigned long flags; | 
|  | 618 |  | 
|  | 619 | /* avoid duplicate entries in the log */ | 
|  | 620 | if (saved_reason & (1 << reason)) | 
|  | 621 | return 0; | 
|  | 622 |  | 
|  | 623 | spin_lock_irqsave(&gsmi_dev.lock, flags); | 
|  | 624 |  | 
|  | 625 | saved_reason |= (1 << reason); | 
|  | 626 |  | 
|  | 627 | /* data pointer */ | 
|  | 628 | memset(gsmi_dev.data_buf->start, 0, gsmi_dev.data_buf->length); | 
|  | 629 | memcpy(gsmi_dev.data_buf->start, &entry, sizeof(entry)); | 
|  | 630 |  | 
|  | 631 | /* parameter buffer */ | 
|  | 632 | param.data_ptr = gsmi_dev.data_buf->address; | 
|  | 633 | memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length); | 
|  | 634 | memcpy(gsmi_dev.param_buf->start, ¶m, sizeof(param)); | 
|  | 635 |  | 
|  | 636 | rc = gsmi_exec(GSMI_CALLBACK, GSMI_CMD_SET_EVENT_LOG); | 
|  | 637 |  | 
|  | 638 | spin_unlock_irqrestore(&gsmi_dev.lock, flags); | 
|  | 639 |  | 
|  | 640 | if (rc < 0) | 
|  | 641 | printk(KERN_ERR "gsmi: Log Shutdown Reason failed\n"); | 
|  | 642 | else | 
|  | 643 | printk(KERN_EMERG "gsmi: Log Shutdown Reason 0x%02x\n", | 
|  | 644 | reason); | 
|  | 645 |  | 
|  | 646 | return rc; | 
|  | 647 | } | 
|  | 648 |  | 
|  | 649 | static int gsmi_reboot_callback(struct notifier_block *nb, | 
|  | 650 | unsigned long reason, void *arg) | 
|  | 651 | { | 
|  | 652 | gsmi_shutdown_reason(GSMI_SHUTDOWN_CLEAN); | 
|  | 653 | return NOTIFY_DONE; | 
|  | 654 | } | 
|  | 655 |  | 
|  | 656 | static struct notifier_block gsmi_reboot_notifier = { | 
|  | 657 | .notifier_call = gsmi_reboot_callback | 
|  | 658 | }; | 
|  | 659 |  | 
|  | 660 | static int gsmi_die_callback(struct notifier_block *nb, | 
|  | 661 | unsigned long reason, void *arg) | 
|  | 662 | { | 
|  | 663 | if (reason == DIE_OOPS) | 
|  | 664 | gsmi_shutdown_reason(GSMI_SHUTDOWN_OOPS); | 
|  | 665 | return NOTIFY_DONE; | 
|  | 666 | } | 
|  | 667 |  | 
|  | 668 | static struct notifier_block gsmi_die_notifier = { | 
|  | 669 | .notifier_call = gsmi_die_callback | 
|  | 670 | }; | 
|  | 671 |  | 
|  | 672 | static int gsmi_panic_callback(struct notifier_block *nb, | 
|  | 673 | unsigned long reason, void *arg) | 
|  | 674 | { | 
|  | 675 | gsmi_shutdown_reason(GSMI_SHUTDOWN_PANIC); | 
|  | 676 | return NOTIFY_DONE; | 
|  | 677 | } | 
|  | 678 |  | 
|  | 679 | static struct notifier_block gsmi_panic_notifier = { | 
|  | 680 | .notifier_call = gsmi_panic_callback, | 
|  | 681 | }; | 
|  | 682 |  | 
|  | 683 | /* | 
|  | 684 | * This hash function was blatantly copied from include/linux/hash.h. | 
|  | 685 | * It is used by this driver to obfuscate a board name that requires a | 
|  | 686 | * quirk within this driver. | 
|  | 687 | * | 
|  | 688 | * Please do not remove this copy of the function as any changes to the | 
|  | 689 | * global utility hash_64() function would break this driver's ability | 
|  | 690 | * to identify a board and provide the appropriate quirk -- mikew@google.com | 
|  | 691 | */ | 
|  | 692 | static u64 __init local_hash_64(u64 val, unsigned bits) | 
|  | 693 | { | 
|  | 694 | u64 hash = val; | 
|  | 695 |  | 
|  | 696 | /*  Sigh, gcc can't optimise this alone like it does for 32 bits. */ | 
|  | 697 | u64 n = hash; | 
|  | 698 | n <<= 18; | 
|  | 699 | hash -= n; | 
|  | 700 | n <<= 33; | 
|  | 701 | hash -= n; | 
|  | 702 | n <<= 3; | 
|  | 703 | hash += n; | 
|  | 704 | n <<= 3; | 
|  | 705 | hash -= n; | 
|  | 706 | n <<= 4; | 
|  | 707 | hash += n; | 
|  | 708 | n <<= 2; | 
|  | 709 | hash += n; | 
|  | 710 |  | 
|  | 711 | /* High bits are more random, so use them. */ | 
|  | 712 | return hash >> (64 - bits); | 
|  | 713 | } | 
|  | 714 |  | 
|  | 715 | static u32 __init hash_oem_table_id(char s[8]) | 
|  | 716 | { | 
|  | 717 | u64 input; | 
|  | 718 | memcpy(&input, s, 8); | 
|  | 719 | return local_hash_64(input, 32); | 
|  | 720 | } | 
|  | 721 |  | 
|  | 722 | static struct dmi_system_id gsmi_dmi_table[] __initdata = { | 
|  | 723 | { | 
|  | 724 | .ident = "Google Board", | 
|  | 725 | .matches = { | 
|  | 726 | DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."), | 
|  | 727 | }, | 
|  | 728 | }, | 
|  | 729 | {} | 
|  | 730 | }; | 
|  | 731 | MODULE_DEVICE_TABLE(dmi, gsmi_dmi_table); | 
|  | 732 |  | 
|  | 733 | static __init int gsmi_system_valid(void) | 
|  | 734 | { | 
|  | 735 | u32 hash; | 
|  | 736 |  | 
|  | 737 | if (!dmi_check_system(gsmi_dmi_table)) | 
|  | 738 | return -ENODEV; | 
|  | 739 |  | 
|  | 740 | /* | 
|  | 741 | * Only newer firmware supports the gsmi interface.  All older | 
|  | 742 | * firmware that didn't support this interface used to plug the | 
|  | 743 | * table name in the first four bytes of the oem_table_id field. | 
|  | 744 | * Newer firmware doesn't do that though, so use that as the | 
|  | 745 | * discriminant factor.  We have to do this in order to | 
|  | 746 | * whitewash our board names out of the public driver. | 
|  | 747 | */ | 
|  | 748 | if (!strncmp(acpi_gbl_FADT.header.oem_table_id, "FACP", 4)) { | 
|  | 749 | printk(KERN_INFO "gsmi: Board is too old\n"); | 
|  | 750 | return -ENODEV; | 
|  | 751 | } | 
|  | 752 |  | 
|  | 753 | /* Disable on board with 1.0 BIOS due to Google bug 2602657 */ | 
|  | 754 | hash = hash_oem_table_id(acpi_gbl_FADT.header.oem_table_id); | 
|  | 755 | if (hash == QUIRKY_BOARD_HASH) { | 
|  | 756 | const char *bios_ver = dmi_get_system_info(DMI_BIOS_VERSION); | 
|  | 757 | if (strncmp(bios_ver, "1.0", 3) == 0) { | 
|  | 758 | pr_info("gsmi: disabled on this board's BIOS %s\n", | 
|  | 759 | bios_ver); | 
|  | 760 | return -ENODEV; | 
|  | 761 | } | 
|  | 762 | } | 
|  | 763 |  | 
|  | 764 | /* check for valid SMI command port in ACPI FADT */ | 
|  | 765 | if (acpi_gbl_FADT.smi_command == 0) { | 
|  | 766 | pr_info("gsmi: missing smi_command\n"); | 
|  | 767 | return -ENODEV; | 
|  | 768 | } | 
|  | 769 |  | 
|  | 770 | /* Found */ | 
|  | 771 | return 0; | 
|  | 772 | } | 
|  | 773 |  | 
|  | 774 | static struct kobject *gsmi_kobj; | 
|  | 775 | static struct efivars efivars; | 
|  | 776 |  | 
|  | 777 | static __init int gsmi_init(void) | 
|  | 778 | { | 
|  | 779 | unsigned long flags; | 
|  | 780 | int ret; | 
|  | 781 |  | 
|  | 782 | ret = gsmi_system_valid(); | 
|  | 783 | if (ret) | 
|  | 784 | return ret; | 
|  | 785 |  | 
|  | 786 | gsmi_dev.smi_cmd = acpi_gbl_FADT.smi_command; | 
|  | 787 |  | 
|  | 788 | /* register device */ | 
|  | 789 | gsmi_dev.pdev = platform_device_register_simple("gsmi", -1, NULL, 0); | 
|  | 790 | if (IS_ERR(gsmi_dev.pdev)) { | 
|  | 791 | printk(KERN_ERR "gsmi: unable to register platform device\n"); | 
|  | 792 | return PTR_ERR(gsmi_dev.pdev); | 
|  | 793 | } | 
|  | 794 |  | 
|  | 795 | /* SMI access needs to be serialized */ | 
|  | 796 | spin_lock_init(&gsmi_dev.lock); | 
|  | 797 |  | 
|  | 798 | /* SMI callbacks require 32bit addresses */ | 
|  | 799 | gsmi_dev.pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); | 
|  | 800 | gsmi_dev.pdev->dev.dma_mask = | 
|  | 801 | &gsmi_dev.pdev->dev.coherent_dma_mask; | 
|  | 802 | ret = -ENOMEM; | 
|  | 803 | gsmi_dev.dma_pool = dma_pool_create("gsmi", &gsmi_dev.pdev->dev, | 
|  | 804 | GSMI_BUF_SIZE, GSMI_BUF_ALIGN, 0); | 
|  | 805 | if (!gsmi_dev.dma_pool) | 
|  | 806 | goto out_err; | 
|  | 807 |  | 
|  | 808 | /* | 
|  | 809 | * pre-allocate buffers because sometimes we are called when | 
|  | 810 | * this is not feasible: oops, panic, die, mce, etc | 
|  | 811 | */ | 
|  | 812 | gsmi_dev.name_buf = gsmi_buf_alloc(); | 
|  | 813 | if (!gsmi_dev.name_buf) { | 
|  | 814 | printk(KERN_ERR "gsmi: failed to allocate name buffer\n"); | 
|  | 815 | goto out_err; | 
|  | 816 | } | 
|  | 817 |  | 
|  | 818 | gsmi_dev.data_buf = gsmi_buf_alloc(); | 
|  | 819 | if (!gsmi_dev.data_buf) { | 
|  | 820 | printk(KERN_ERR "gsmi: failed to allocate data buffer\n"); | 
|  | 821 | goto out_err; | 
|  | 822 | } | 
|  | 823 |  | 
|  | 824 | gsmi_dev.param_buf = gsmi_buf_alloc(); | 
|  | 825 | if (!gsmi_dev.param_buf) { | 
|  | 826 | printk(KERN_ERR "gsmi: failed to allocate param buffer\n"); | 
|  | 827 | goto out_err; | 
|  | 828 | } | 
|  | 829 |  | 
|  | 830 | /* | 
|  | 831 | * Determine type of handshake used to serialize the SMI | 
|  | 832 | * entry. See also gsmi_exec(). | 
|  | 833 | * | 
|  | 834 | * There's a "behavior" present on some chipsets where writing the | 
|  | 835 | * SMI trigger register in the southbridge doesn't result in an | 
|  | 836 | * immediate SMI. Rather, the processor can execute "a few" more | 
|  | 837 | * instructions before the SMI takes effect. To ensure synchronous | 
|  | 838 | * behavior, implement a handshake between the kernel driver and the | 
|  | 839 | * firmware handler to spin until released. This ioctl determines | 
|  | 840 | * the type of handshake. | 
|  | 841 | * | 
|  | 842 | * NONE: The firmware handler does not implement any | 
|  | 843 | * handshake. Either it doesn't need to, or it's legacy firmware | 
|  | 844 | * that doesn't know it needs to and never will. | 
|  | 845 | * | 
|  | 846 | * CF: The firmware handler will clear the CF in the saved | 
|  | 847 | * state before returning. The driver may set the CF and test for | 
|  | 848 | * it to clear before proceeding. | 
|  | 849 | * | 
|  | 850 | * SPIN: The firmware handler does not implement any handshake | 
|  | 851 | * but the driver should spin for a hundred or so microseconds | 
|  | 852 | * to ensure the SMI has triggered. | 
|  | 853 | * | 
|  | 854 | * Finally, the handler will return -ENOSYS if | 
|  | 855 | * GSMI_CMD_HANDSHAKE_TYPE is unimplemented, which implies | 
|  | 856 | * HANDSHAKE_NONE. | 
|  | 857 | */ | 
|  | 858 | spin_lock_irqsave(&gsmi_dev.lock, flags); | 
|  | 859 | gsmi_dev.handshake_type = GSMI_HANDSHAKE_SPIN; | 
|  | 860 | gsmi_dev.handshake_type = | 
|  | 861 | gsmi_exec(GSMI_CALLBACK, GSMI_CMD_HANDSHAKE_TYPE); | 
|  | 862 | if (gsmi_dev.handshake_type == -ENOSYS) | 
|  | 863 | gsmi_dev.handshake_type = GSMI_HANDSHAKE_NONE; | 
|  | 864 | spin_unlock_irqrestore(&gsmi_dev.lock, flags); | 
|  | 865 |  | 
|  | 866 | /* Remove and clean up gsmi if the handshake could not complete. */ | 
|  | 867 | if (gsmi_dev.handshake_type == -ENXIO) { | 
|  | 868 | printk(KERN_INFO "gsmi version " DRIVER_VERSION | 
|  | 869 | " failed to load\n"); | 
|  | 870 | ret = -ENODEV; | 
|  | 871 | goto out_err; | 
|  | 872 | } | 
|  | 873 |  | 
| Mike Waychison | 74c5b31 | 2011-04-29 17:39:19 -0700 | [diff] [blame] | 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"); | 
| Axel Lin | 1e4de81 | 2011-06-29 15:57:53 +0800 | [diff] [blame] | 893 | goto out_remove_bin_file; | 
| Mike Waychison | 74c5b31 | 2011-04-29 17:39:19 -0700 | [diff] [blame] | 894 | } | 
|  | 895 |  | 
| Axel Lin | 1e4de81 | 2011-06-29 15:57:53 +0800 | [diff] [blame] | 896 | ret = register_efivars(&efivars, &efivar_ops, gsmi_kobj); | 
|  | 897 | if (ret) { | 
| Mike Waychison | 74c5b31 | 2011-04-29 17:39:19 -0700 | [diff] [blame] | 898 | printk(KERN_INFO "gsmi: Failed to register efivars\n"); | 
| Axel Lin | 1e4de81 | 2011-06-29 15:57:53 +0800 | [diff] [blame] | 899 | goto out_remove_sysfs_files; | 
| Mike Waychison | 74c5b31 | 2011-04-29 17:39:19 -0700 | [diff] [blame] | 900 | } | 
|  | 901 |  | 
|  | 902 | register_reboot_notifier(&gsmi_reboot_notifier); | 
|  | 903 | register_die_notifier(&gsmi_die_notifier); | 
|  | 904 | atomic_notifier_chain_register(&panic_notifier_list, | 
|  | 905 | &gsmi_panic_notifier); | 
|  | 906 |  | 
| Axel Lin | 1e4de81 | 2011-06-29 15:57:53 +0800 | [diff] [blame] | 907 | printk(KERN_INFO "gsmi version " DRIVER_VERSION " loaded\n"); | 
|  | 908 |  | 
| Mike Waychison | 74c5b31 | 2011-04-29 17:39:19 -0700 | [diff] [blame] | 909 | return 0; | 
|  | 910 |  | 
| Axel Lin | 1e4de81 | 2011-06-29 15:57:53 +0800 | [diff] [blame] | 911 | out_remove_sysfs_files: | 
|  | 912 | sysfs_remove_files(gsmi_kobj, gsmi_attrs); | 
|  | 913 | out_remove_bin_file: | 
|  | 914 | sysfs_remove_bin_file(gsmi_kobj, &eventlog_bin_attr); | 
|  | 915 | out_err: | 
| Mike Waychison | 74c5b31 | 2011-04-29 17:39:19 -0700 | [diff] [blame] | 916 | kobject_put(gsmi_kobj); | 
|  | 917 | gsmi_buf_free(gsmi_dev.param_buf); | 
|  | 918 | gsmi_buf_free(gsmi_dev.data_buf); | 
|  | 919 | gsmi_buf_free(gsmi_dev.name_buf); | 
|  | 920 | if (gsmi_dev.dma_pool) | 
|  | 921 | dma_pool_destroy(gsmi_dev.dma_pool); | 
|  | 922 | platform_device_unregister(gsmi_dev.pdev); | 
|  | 923 | pr_info("gsmi: failed to load: %d\n", ret); | 
|  | 924 | return ret; | 
|  | 925 | } | 
|  | 926 |  | 
|  | 927 | static void __exit gsmi_exit(void) | 
|  | 928 | { | 
|  | 929 | unregister_reboot_notifier(&gsmi_reboot_notifier); | 
|  | 930 | unregister_die_notifier(&gsmi_die_notifier); | 
|  | 931 | atomic_notifier_chain_unregister(&panic_notifier_list, | 
|  | 932 | &gsmi_panic_notifier); | 
|  | 933 | unregister_efivars(&efivars); | 
|  | 934 |  | 
| Axel Lin | 1e4de81 | 2011-06-29 15:57:53 +0800 | [diff] [blame] | 935 | sysfs_remove_files(gsmi_kobj, gsmi_attrs); | 
|  | 936 | sysfs_remove_bin_file(gsmi_kobj, &eventlog_bin_attr); | 
| Mike Waychison | 74c5b31 | 2011-04-29 17:39:19 -0700 | [diff] [blame] | 937 | kobject_put(gsmi_kobj); | 
|  | 938 | gsmi_buf_free(gsmi_dev.param_buf); | 
|  | 939 | gsmi_buf_free(gsmi_dev.data_buf); | 
|  | 940 | gsmi_buf_free(gsmi_dev.name_buf); | 
|  | 941 | dma_pool_destroy(gsmi_dev.dma_pool); | 
|  | 942 | platform_device_unregister(gsmi_dev.pdev); | 
|  | 943 | } | 
|  | 944 |  | 
|  | 945 | module_init(gsmi_init); | 
|  | 946 | module_exit(gsmi_exit); | 
|  | 947 |  | 
|  | 948 | MODULE_AUTHOR("Google, Inc."); | 
|  | 949 | MODULE_LICENSE("GPL"); |