1a08f82d0SHuang Ying /* 2a08f82d0SHuang Ying * APEI Error Record Serialization Table support 3a08f82d0SHuang Ying * 4a08f82d0SHuang Ying * ERST is a way provided by APEI to save and retrieve hardware error 558f87ed0SLucas De Marchi * information to and from a persistent store. 6a08f82d0SHuang Ying * 7a08f82d0SHuang Ying * For more information about ERST, please refer to ACPI Specification 8a08f82d0SHuang Ying * version 4.0, section 17.4. 9a08f82d0SHuang Ying * 10a08f82d0SHuang Ying * Copyright 2010 Intel Corp. 11a08f82d0SHuang Ying * Author: Huang Ying <ying.huang@intel.com> 12a08f82d0SHuang Ying * 13a08f82d0SHuang Ying * This program is free software; you can redistribute it and/or 14a08f82d0SHuang Ying * modify it under the terms of the GNU General Public License version 15a08f82d0SHuang Ying * 2 as published by the Free Software Foundation. 16a08f82d0SHuang Ying * 17a08f82d0SHuang Ying * This program is distributed in the hope that it will be useful, 18a08f82d0SHuang Ying * but WITHOUT ANY WARRANTY; without even the implied warranty of 19a08f82d0SHuang Ying * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20a08f82d0SHuang Ying * GNU General Public License for more details. 21a08f82d0SHuang Ying * 22a08f82d0SHuang Ying * You should have received a copy of the GNU General Public License 23a08f82d0SHuang Ying * along with this program; if not, write to the Free Software 24a08f82d0SHuang Ying * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 25a08f82d0SHuang Ying */ 26a08f82d0SHuang Ying 27a08f82d0SHuang Ying #include <linux/kernel.h> 28a08f82d0SHuang Ying #include <linux/module.h> 29a08f82d0SHuang Ying #include <linux/init.h> 30a08f82d0SHuang Ying #include <linux/delay.h> 31a08f82d0SHuang Ying #include <linux/io.h> 32a08f82d0SHuang Ying #include <linux/acpi.h> 33a08f82d0SHuang Ying #include <linux/uaccess.h> 34a08f82d0SHuang Ying #include <linux/cper.h> 35a08f82d0SHuang Ying #include <linux/nmi.h> 360a7992c9SThomas Gleixner #include <linux/hardirq.h> 370bb77c46STony Luck #include <linux/pstore.h> 38a08f82d0SHuang Ying #include <acpi/apei.h> 39a08f82d0SHuang Ying 40a08f82d0SHuang Ying #include "apei-internal.h" 41a08f82d0SHuang Ying 42a08f82d0SHuang Ying #define ERST_PFX "ERST: " 43a08f82d0SHuang Ying 44a08f82d0SHuang Ying /* ERST command status */ 45a08f82d0SHuang Ying #define ERST_STATUS_SUCCESS 0x0 46a08f82d0SHuang Ying #define ERST_STATUS_NOT_ENOUGH_SPACE 0x1 47a08f82d0SHuang Ying #define ERST_STATUS_HARDWARE_NOT_AVAILABLE 0x2 48a08f82d0SHuang Ying #define ERST_STATUS_FAILED 0x3 49a08f82d0SHuang Ying #define ERST_STATUS_RECORD_STORE_EMPTY 0x4 50a08f82d0SHuang Ying #define ERST_STATUS_RECORD_NOT_FOUND 0x5 51a08f82d0SHuang Ying 52a08f82d0SHuang Ying #define ERST_TAB_ENTRY(tab) \ 53a08f82d0SHuang Ying ((struct acpi_whea_header *)((char *)(tab) + \ 54a08f82d0SHuang Ying sizeof(struct acpi_table_erst))) 55a08f82d0SHuang Ying 56a08f82d0SHuang Ying #define SPIN_UNIT 100 /* 100ns */ 57a08f82d0SHuang Ying /* Firmware should respond within 1 miliseconds */ 58a08f82d0SHuang Ying #define FIRMWARE_TIMEOUT (1 * NSEC_PER_MSEC) 59a08f82d0SHuang Ying #define FIRMWARE_MAX_STALL 50 /* 50us */ 60a08f82d0SHuang Ying 61a08f82d0SHuang Ying int erst_disable; 62a08f82d0SHuang Ying EXPORT_SYMBOL_GPL(erst_disable); 63a08f82d0SHuang Ying 64a08f82d0SHuang Ying static struct acpi_table_erst *erst_tab; 65a08f82d0SHuang Ying 66a08f82d0SHuang Ying /* ERST Error Log Address Range atrributes */ 67a08f82d0SHuang Ying #define ERST_RANGE_RESERVED 0x0001 68a08f82d0SHuang Ying #define ERST_RANGE_NVRAM 0x0002 69a08f82d0SHuang Ying #define ERST_RANGE_SLOW 0x0004 70a08f82d0SHuang Ying 71a08f82d0SHuang Ying /* 72a08f82d0SHuang Ying * ERST Error Log Address Range, used as buffer for reading/writing 73a08f82d0SHuang Ying * error records. 74a08f82d0SHuang Ying */ 75a08f82d0SHuang Ying static struct erst_erange { 76a08f82d0SHuang Ying u64 base; 77a08f82d0SHuang Ying u64 size; 78a08f82d0SHuang Ying void __iomem *vaddr; 79a08f82d0SHuang Ying u32 attr; 80a08f82d0SHuang Ying } erst_erange; 81a08f82d0SHuang Ying 82a08f82d0SHuang Ying /* 83a08f82d0SHuang Ying * Prevent ERST interpreter to run simultaneously, because the 84a08f82d0SHuang Ying * corresponding firmware implementation may not work properly when 85a08f82d0SHuang Ying * invoked simultaneously. 86a08f82d0SHuang Ying * 87a08f82d0SHuang Ying * It is used to provide exclusive accessing for ERST Error Log 88a08f82d0SHuang Ying * Address Range too. 89a08f82d0SHuang Ying */ 903b38bb5fSHuang Ying static DEFINE_RAW_SPINLOCK(erst_lock); 91a08f82d0SHuang Ying 92a08f82d0SHuang Ying static inline int erst_errno(int command_status) 93a08f82d0SHuang Ying { 94a08f82d0SHuang Ying switch (command_status) { 95a08f82d0SHuang Ying case ERST_STATUS_SUCCESS: 96a08f82d0SHuang Ying return 0; 97a08f82d0SHuang Ying case ERST_STATUS_HARDWARE_NOT_AVAILABLE: 98a08f82d0SHuang Ying return -ENODEV; 99a08f82d0SHuang Ying case ERST_STATUS_NOT_ENOUGH_SPACE: 100a08f82d0SHuang Ying return -ENOSPC; 101a08f82d0SHuang Ying case ERST_STATUS_RECORD_STORE_EMPTY: 102a08f82d0SHuang Ying case ERST_STATUS_RECORD_NOT_FOUND: 103a08f82d0SHuang Ying return -ENOENT; 104a08f82d0SHuang Ying default: 105a08f82d0SHuang Ying return -EINVAL; 106a08f82d0SHuang Ying } 107a08f82d0SHuang Ying } 108a08f82d0SHuang Ying 109a08f82d0SHuang Ying static int erst_timedout(u64 *t, u64 spin_unit) 110a08f82d0SHuang Ying { 111a08f82d0SHuang Ying if ((s64)*t < spin_unit) { 112a08f82d0SHuang Ying pr_warning(FW_WARN ERST_PFX 113a08f82d0SHuang Ying "Firmware does not respond in time\n"); 114a08f82d0SHuang Ying return 1; 115a08f82d0SHuang Ying } 116a08f82d0SHuang Ying *t -= spin_unit; 117a08f82d0SHuang Ying ndelay(spin_unit); 118a08f82d0SHuang Ying touch_nmi_watchdog(); 119a08f82d0SHuang Ying return 0; 120a08f82d0SHuang Ying } 121a08f82d0SHuang Ying 122a08f82d0SHuang Ying static int erst_exec_load_var1(struct apei_exec_context *ctx, 123a08f82d0SHuang Ying struct acpi_whea_header *entry) 124a08f82d0SHuang Ying { 125a08f82d0SHuang Ying return __apei_exec_read_register(entry, &ctx->var1); 126a08f82d0SHuang Ying } 127a08f82d0SHuang Ying 128a08f82d0SHuang Ying static int erst_exec_load_var2(struct apei_exec_context *ctx, 129a08f82d0SHuang Ying struct acpi_whea_header *entry) 130a08f82d0SHuang Ying { 131a08f82d0SHuang Ying return __apei_exec_read_register(entry, &ctx->var2); 132a08f82d0SHuang Ying } 133a08f82d0SHuang Ying 134a08f82d0SHuang Ying static int erst_exec_store_var1(struct apei_exec_context *ctx, 135a08f82d0SHuang Ying struct acpi_whea_header *entry) 136a08f82d0SHuang Ying { 137a08f82d0SHuang Ying return __apei_exec_write_register(entry, ctx->var1); 138a08f82d0SHuang Ying } 139a08f82d0SHuang Ying 140a08f82d0SHuang Ying static int erst_exec_add(struct apei_exec_context *ctx, 141a08f82d0SHuang Ying struct acpi_whea_header *entry) 142a08f82d0SHuang Ying { 143a08f82d0SHuang Ying ctx->var1 += ctx->var2; 144a08f82d0SHuang Ying return 0; 145a08f82d0SHuang Ying } 146a08f82d0SHuang Ying 147a08f82d0SHuang Ying static int erst_exec_subtract(struct apei_exec_context *ctx, 148a08f82d0SHuang Ying struct acpi_whea_header *entry) 149a08f82d0SHuang Ying { 150a08f82d0SHuang Ying ctx->var1 -= ctx->var2; 151a08f82d0SHuang Ying return 0; 152a08f82d0SHuang Ying } 153a08f82d0SHuang Ying 154a08f82d0SHuang Ying static int erst_exec_add_value(struct apei_exec_context *ctx, 155a08f82d0SHuang Ying struct acpi_whea_header *entry) 156a08f82d0SHuang Ying { 157a08f82d0SHuang Ying int rc; 158a08f82d0SHuang Ying u64 val; 159a08f82d0SHuang Ying 160a08f82d0SHuang Ying rc = __apei_exec_read_register(entry, &val); 161a08f82d0SHuang Ying if (rc) 162a08f82d0SHuang Ying return rc; 163a08f82d0SHuang Ying val += ctx->value; 164a08f82d0SHuang Ying rc = __apei_exec_write_register(entry, val); 165a08f82d0SHuang Ying return rc; 166a08f82d0SHuang Ying } 167a08f82d0SHuang Ying 168a08f82d0SHuang Ying static int erst_exec_subtract_value(struct apei_exec_context *ctx, 169a08f82d0SHuang Ying struct acpi_whea_header *entry) 170a08f82d0SHuang Ying { 171a08f82d0SHuang Ying int rc; 172a08f82d0SHuang Ying u64 val; 173a08f82d0SHuang Ying 174a08f82d0SHuang Ying rc = __apei_exec_read_register(entry, &val); 175a08f82d0SHuang Ying if (rc) 176a08f82d0SHuang Ying return rc; 177a08f82d0SHuang Ying val -= ctx->value; 178a08f82d0SHuang Ying rc = __apei_exec_write_register(entry, val); 179a08f82d0SHuang Ying return rc; 180a08f82d0SHuang Ying } 181a08f82d0SHuang Ying 182a08f82d0SHuang Ying static int erst_exec_stall(struct apei_exec_context *ctx, 183a08f82d0SHuang Ying struct acpi_whea_header *entry) 184a08f82d0SHuang Ying { 185a08f82d0SHuang Ying u64 stall_time; 186a08f82d0SHuang Ying 187a08f82d0SHuang Ying if (ctx->value > FIRMWARE_MAX_STALL) { 188a08f82d0SHuang Ying if (!in_nmi()) 189a08f82d0SHuang Ying pr_warning(FW_WARN ERST_PFX 190a08f82d0SHuang Ying "Too long stall time for stall instruction: %llx.\n", 191a08f82d0SHuang Ying ctx->value); 192a08f82d0SHuang Ying stall_time = FIRMWARE_MAX_STALL; 193a08f82d0SHuang Ying } else 194a08f82d0SHuang Ying stall_time = ctx->value; 195a08f82d0SHuang Ying udelay(stall_time); 196a08f82d0SHuang Ying return 0; 197a08f82d0SHuang Ying } 198a08f82d0SHuang Ying 199a08f82d0SHuang Ying static int erst_exec_stall_while_true(struct apei_exec_context *ctx, 200a08f82d0SHuang Ying struct acpi_whea_header *entry) 201a08f82d0SHuang Ying { 202a08f82d0SHuang Ying int rc; 203a08f82d0SHuang Ying u64 val; 204a08f82d0SHuang Ying u64 timeout = FIRMWARE_TIMEOUT; 205a08f82d0SHuang Ying u64 stall_time; 206a08f82d0SHuang Ying 207a08f82d0SHuang Ying if (ctx->var1 > FIRMWARE_MAX_STALL) { 208a08f82d0SHuang Ying if (!in_nmi()) 209a08f82d0SHuang Ying pr_warning(FW_WARN ERST_PFX 210a08f82d0SHuang Ying "Too long stall time for stall while true instruction: %llx.\n", 211a08f82d0SHuang Ying ctx->var1); 212a08f82d0SHuang Ying stall_time = FIRMWARE_MAX_STALL; 213a08f82d0SHuang Ying } else 214a08f82d0SHuang Ying stall_time = ctx->var1; 215a08f82d0SHuang Ying 216a08f82d0SHuang Ying for (;;) { 217a08f82d0SHuang Ying rc = __apei_exec_read_register(entry, &val); 218a08f82d0SHuang Ying if (rc) 219a08f82d0SHuang Ying return rc; 220a08f82d0SHuang Ying if (val != ctx->value) 221a08f82d0SHuang Ying break; 222a08f82d0SHuang Ying if (erst_timedout(&timeout, stall_time * NSEC_PER_USEC)) 223a08f82d0SHuang Ying return -EIO; 224a08f82d0SHuang Ying } 225a08f82d0SHuang Ying return 0; 226a08f82d0SHuang Ying } 227a08f82d0SHuang Ying 228a08f82d0SHuang Ying static int erst_exec_skip_next_instruction_if_true( 229a08f82d0SHuang Ying struct apei_exec_context *ctx, 230a08f82d0SHuang Ying struct acpi_whea_header *entry) 231a08f82d0SHuang Ying { 232a08f82d0SHuang Ying int rc; 233a08f82d0SHuang Ying u64 val; 234a08f82d0SHuang Ying 235a08f82d0SHuang Ying rc = __apei_exec_read_register(entry, &val); 236a08f82d0SHuang Ying if (rc) 237a08f82d0SHuang Ying return rc; 238a08f82d0SHuang Ying if (val == ctx->value) { 239a08f82d0SHuang Ying ctx->ip += 2; 240a08f82d0SHuang Ying return APEI_EXEC_SET_IP; 241a08f82d0SHuang Ying } 242a08f82d0SHuang Ying 243a08f82d0SHuang Ying return 0; 244a08f82d0SHuang Ying } 245a08f82d0SHuang Ying 246a08f82d0SHuang Ying static int erst_exec_goto(struct apei_exec_context *ctx, 247a08f82d0SHuang Ying struct acpi_whea_header *entry) 248a08f82d0SHuang Ying { 249a08f82d0SHuang Ying ctx->ip = ctx->value; 250a08f82d0SHuang Ying return APEI_EXEC_SET_IP; 251a08f82d0SHuang Ying } 252a08f82d0SHuang Ying 253a08f82d0SHuang Ying static int erst_exec_set_src_address_base(struct apei_exec_context *ctx, 254a08f82d0SHuang Ying struct acpi_whea_header *entry) 255a08f82d0SHuang Ying { 256a08f82d0SHuang Ying return __apei_exec_read_register(entry, &ctx->src_base); 257a08f82d0SHuang Ying } 258a08f82d0SHuang Ying 259a08f82d0SHuang Ying static int erst_exec_set_dst_address_base(struct apei_exec_context *ctx, 260a08f82d0SHuang Ying struct acpi_whea_header *entry) 261a08f82d0SHuang Ying { 262a08f82d0SHuang Ying return __apei_exec_read_register(entry, &ctx->dst_base); 263a08f82d0SHuang Ying } 264a08f82d0SHuang Ying 265a08f82d0SHuang Ying static int erst_exec_move_data(struct apei_exec_context *ctx, 266a08f82d0SHuang Ying struct acpi_whea_header *entry) 267a08f82d0SHuang Ying { 268a08f82d0SHuang Ying int rc; 269a08f82d0SHuang Ying u64 offset; 2700bbba38aSHuang Ying void *src, *dst; 2710bbba38aSHuang Ying 2720bbba38aSHuang Ying /* ioremap does not work in interrupt context */ 2730bbba38aSHuang Ying if (in_interrupt()) { 2740bbba38aSHuang Ying pr_warning(ERST_PFX 2750bbba38aSHuang Ying "MOVE_DATA can not be used in interrupt context"); 2760bbba38aSHuang Ying return -EBUSY; 2770bbba38aSHuang Ying } 278a08f82d0SHuang Ying 279a08f82d0SHuang Ying rc = __apei_exec_read_register(entry, &offset); 280a08f82d0SHuang Ying if (rc) 281a08f82d0SHuang Ying return rc; 2820bbba38aSHuang Ying 2830bbba38aSHuang Ying src = ioremap(ctx->src_base + offset, ctx->var2); 2840bbba38aSHuang Ying if (!src) 2850bbba38aSHuang Ying return -ENOMEM; 2860bbba38aSHuang Ying dst = ioremap(ctx->dst_base + offset, ctx->var2); 2870bbba38aSHuang Ying if (!dst) 2880bbba38aSHuang Ying return -ENOMEM; 2890bbba38aSHuang Ying 2900bbba38aSHuang Ying memmove(dst, src, ctx->var2); 2910bbba38aSHuang Ying 2920bbba38aSHuang Ying iounmap(src); 2930bbba38aSHuang Ying iounmap(dst); 294a08f82d0SHuang Ying 295a08f82d0SHuang Ying return 0; 296a08f82d0SHuang Ying } 297a08f82d0SHuang Ying 298a08f82d0SHuang Ying static struct apei_exec_ins_type erst_ins_type[] = { 299a08f82d0SHuang Ying [ACPI_ERST_READ_REGISTER] = { 300a08f82d0SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER, 301a08f82d0SHuang Ying .run = apei_exec_read_register, 302a08f82d0SHuang Ying }, 303a08f82d0SHuang Ying [ACPI_ERST_READ_REGISTER_VALUE] = { 304a08f82d0SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER, 305a08f82d0SHuang Ying .run = apei_exec_read_register_value, 306a08f82d0SHuang Ying }, 307a08f82d0SHuang Ying [ACPI_ERST_WRITE_REGISTER] = { 308a08f82d0SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER, 309a08f82d0SHuang Ying .run = apei_exec_write_register, 310a08f82d0SHuang Ying }, 311a08f82d0SHuang Ying [ACPI_ERST_WRITE_REGISTER_VALUE] = { 312a08f82d0SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER, 313a08f82d0SHuang Ying .run = apei_exec_write_register_value, 314a08f82d0SHuang Ying }, 315a08f82d0SHuang Ying [ACPI_ERST_NOOP] = { 316a08f82d0SHuang Ying .flags = 0, 317a08f82d0SHuang Ying .run = apei_exec_noop, 318a08f82d0SHuang Ying }, 319a08f82d0SHuang Ying [ACPI_ERST_LOAD_VAR1] = { 320a08f82d0SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER, 321a08f82d0SHuang Ying .run = erst_exec_load_var1, 322a08f82d0SHuang Ying }, 323a08f82d0SHuang Ying [ACPI_ERST_LOAD_VAR2] = { 324a08f82d0SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER, 325a08f82d0SHuang Ying .run = erst_exec_load_var2, 326a08f82d0SHuang Ying }, 327a08f82d0SHuang Ying [ACPI_ERST_STORE_VAR1] = { 328a08f82d0SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER, 329a08f82d0SHuang Ying .run = erst_exec_store_var1, 330a08f82d0SHuang Ying }, 331a08f82d0SHuang Ying [ACPI_ERST_ADD] = { 332a08f82d0SHuang Ying .flags = 0, 333a08f82d0SHuang Ying .run = erst_exec_add, 334a08f82d0SHuang Ying }, 335a08f82d0SHuang Ying [ACPI_ERST_SUBTRACT] = { 336a08f82d0SHuang Ying .flags = 0, 337a08f82d0SHuang Ying .run = erst_exec_subtract, 338a08f82d0SHuang Ying }, 339a08f82d0SHuang Ying [ACPI_ERST_ADD_VALUE] = { 340a08f82d0SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER, 341a08f82d0SHuang Ying .run = erst_exec_add_value, 342a08f82d0SHuang Ying }, 343a08f82d0SHuang Ying [ACPI_ERST_SUBTRACT_VALUE] = { 344a08f82d0SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER, 345a08f82d0SHuang Ying .run = erst_exec_subtract_value, 346a08f82d0SHuang Ying }, 347a08f82d0SHuang Ying [ACPI_ERST_STALL] = { 348a08f82d0SHuang Ying .flags = 0, 349a08f82d0SHuang Ying .run = erst_exec_stall, 350a08f82d0SHuang Ying }, 351a08f82d0SHuang Ying [ACPI_ERST_STALL_WHILE_TRUE] = { 352a08f82d0SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER, 353a08f82d0SHuang Ying .run = erst_exec_stall_while_true, 354a08f82d0SHuang Ying }, 355a08f82d0SHuang Ying [ACPI_ERST_SKIP_NEXT_IF_TRUE] = { 356a08f82d0SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER, 357a08f82d0SHuang Ying .run = erst_exec_skip_next_instruction_if_true, 358a08f82d0SHuang Ying }, 359a08f82d0SHuang Ying [ACPI_ERST_GOTO] = { 360a08f82d0SHuang Ying .flags = 0, 361a08f82d0SHuang Ying .run = erst_exec_goto, 362a08f82d0SHuang Ying }, 363a08f82d0SHuang Ying [ACPI_ERST_SET_SRC_ADDRESS_BASE] = { 364a08f82d0SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER, 365a08f82d0SHuang Ying .run = erst_exec_set_src_address_base, 366a08f82d0SHuang Ying }, 367a08f82d0SHuang Ying [ACPI_ERST_SET_DST_ADDRESS_BASE] = { 368a08f82d0SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER, 369a08f82d0SHuang Ying .run = erst_exec_set_dst_address_base, 370a08f82d0SHuang Ying }, 371a08f82d0SHuang Ying [ACPI_ERST_MOVE_DATA] = { 372a08f82d0SHuang Ying .flags = APEI_EXEC_INS_ACCESS_REGISTER, 373a08f82d0SHuang Ying .run = erst_exec_move_data, 374a08f82d0SHuang Ying }, 375a08f82d0SHuang Ying }; 376a08f82d0SHuang Ying 377a08f82d0SHuang Ying static inline void erst_exec_ctx_init(struct apei_exec_context *ctx) 378a08f82d0SHuang Ying { 379a08f82d0SHuang Ying apei_exec_ctx_init(ctx, erst_ins_type, ARRAY_SIZE(erst_ins_type), 380a08f82d0SHuang Ying ERST_TAB_ENTRY(erst_tab), erst_tab->entries); 381a08f82d0SHuang Ying } 382a08f82d0SHuang Ying 383a08f82d0SHuang Ying static int erst_get_erange(struct erst_erange *range) 384a08f82d0SHuang Ying { 385a08f82d0SHuang Ying struct apei_exec_context ctx; 386a08f82d0SHuang Ying int rc; 387a08f82d0SHuang Ying 388a08f82d0SHuang Ying erst_exec_ctx_init(&ctx); 389a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_RANGE); 390a08f82d0SHuang Ying if (rc) 391a08f82d0SHuang Ying return rc; 392a08f82d0SHuang Ying range->base = apei_exec_ctx_get_output(&ctx); 393a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_LENGTH); 394a08f82d0SHuang Ying if (rc) 395a08f82d0SHuang Ying return rc; 396a08f82d0SHuang Ying range->size = apei_exec_ctx_get_output(&ctx); 397a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_ATTRIBUTES); 398a08f82d0SHuang Ying if (rc) 399a08f82d0SHuang Ying return rc; 400a08f82d0SHuang Ying range->attr = apei_exec_ctx_get_output(&ctx); 401a08f82d0SHuang Ying 402a08f82d0SHuang Ying return 0; 403a08f82d0SHuang Ying } 404a08f82d0SHuang Ying 405a08f82d0SHuang Ying static ssize_t __erst_get_record_count(void) 406a08f82d0SHuang Ying { 407a08f82d0SHuang Ying struct apei_exec_context ctx; 408a08f82d0SHuang Ying int rc; 409a08f82d0SHuang Ying 410a08f82d0SHuang Ying erst_exec_ctx_init(&ctx); 411a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_COUNT); 412a08f82d0SHuang Ying if (rc) 413a08f82d0SHuang Ying return rc; 414a08f82d0SHuang Ying return apei_exec_ctx_get_output(&ctx); 415a08f82d0SHuang Ying } 416a08f82d0SHuang Ying 417a08f82d0SHuang Ying ssize_t erst_get_record_count(void) 418a08f82d0SHuang Ying { 419a08f82d0SHuang Ying ssize_t count; 420a08f82d0SHuang Ying unsigned long flags; 421a08f82d0SHuang Ying 422a08f82d0SHuang Ying if (erst_disable) 423a08f82d0SHuang Ying return -ENODEV; 424a08f82d0SHuang Ying 4253b38bb5fSHuang Ying raw_spin_lock_irqsave(&erst_lock, flags); 426a08f82d0SHuang Ying count = __erst_get_record_count(); 4273b38bb5fSHuang Ying raw_spin_unlock_irqrestore(&erst_lock, flags); 428a08f82d0SHuang Ying 429a08f82d0SHuang Ying return count; 430a08f82d0SHuang Ying } 431a08f82d0SHuang Ying EXPORT_SYMBOL_GPL(erst_get_record_count); 432a08f82d0SHuang Ying 433a08f82d0SHuang Ying static int __erst_get_next_record_id(u64 *record_id) 434a08f82d0SHuang Ying { 435a08f82d0SHuang Ying struct apei_exec_context ctx; 436a08f82d0SHuang Ying int rc; 437a08f82d0SHuang Ying 438a08f82d0SHuang Ying erst_exec_ctx_init(&ctx); 439a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_ID); 440a08f82d0SHuang Ying if (rc) 441a08f82d0SHuang Ying return rc; 442a08f82d0SHuang Ying *record_id = apei_exec_ctx_get_output(&ctx); 443a08f82d0SHuang Ying 444a08f82d0SHuang Ying return 0; 445a08f82d0SHuang Ying } 446a08f82d0SHuang Ying 447a08f82d0SHuang Ying /* 448a08f82d0SHuang Ying * Get the record ID of an existing error record on the persistent 449a08f82d0SHuang Ying * storage. If there is no error record on the persistent storage, the 450a08f82d0SHuang Ying * returned record_id is APEI_ERST_INVALID_RECORD_ID. 451a08f82d0SHuang Ying */ 452a08f82d0SHuang Ying int erst_get_next_record_id(u64 *record_id) 453a08f82d0SHuang Ying { 454a08f82d0SHuang Ying int rc; 455a08f82d0SHuang Ying unsigned long flags; 456a08f82d0SHuang Ying 457a08f82d0SHuang Ying if (erst_disable) 458a08f82d0SHuang Ying return -ENODEV; 459a08f82d0SHuang Ying 4603b38bb5fSHuang Ying raw_spin_lock_irqsave(&erst_lock, flags); 461a08f82d0SHuang Ying rc = __erst_get_next_record_id(record_id); 4623b38bb5fSHuang Ying raw_spin_unlock_irqrestore(&erst_lock, flags); 463a08f82d0SHuang Ying 464a08f82d0SHuang Ying return rc; 465a08f82d0SHuang Ying } 466a08f82d0SHuang Ying EXPORT_SYMBOL_GPL(erst_get_next_record_id); 467a08f82d0SHuang Ying 468a08f82d0SHuang Ying static int __erst_write_to_storage(u64 offset) 469a08f82d0SHuang Ying { 470a08f82d0SHuang Ying struct apei_exec_context ctx; 471a08f82d0SHuang Ying u64 timeout = FIRMWARE_TIMEOUT; 472a08f82d0SHuang Ying u64 val; 473a08f82d0SHuang Ying int rc; 474a08f82d0SHuang Ying 475a08f82d0SHuang Ying erst_exec_ctx_init(&ctx); 476a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_WRITE); 477a08f82d0SHuang Ying if (rc) 478a08f82d0SHuang Ying return rc; 479a08f82d0SHuang Ying apei_exec_ctx_set_input(&ctx, offset); 480a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET); 481a08f82d0SHuang Ying if (rc) 482a08f82d0SHuang Ying return rc; 483a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION); 484a08f82d0SHuang Ying if (rc) 485a08f82d0SHuang Ying return rc; 486a08f82d0SHuang Ying for (;;) { 487a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS); 488a08f82d0SHuang Ying if (rc) 489a08f82d0SHuang Ying return rc; 490a08f82d0SHuang Ying val = apei_exec_ctx_get_output(&ctx); 491a08f82d0SHuang Ying if (!val) 492a08f82d0SHuang Ying break; 493a08f82d0SHuang Ying if (erst_timedout(&timeout, SPIN_UNIT)) 494a08f82d0SHuang Ying return -EIO; 495a08f82d0SHuang Ying } 496a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS); 497a08f82d0SHuang Ying if (rc) 498a08f82d0SHuang Ying return rc; 499a08f82d0SHuang Ying val = apei_exec_ctx_get_output(&ctx); 500a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_END); 501a08f82d0SHuang Ying if (rc) 502a08f82d0SHuang Ying return rc; 503a08f82d0SHuang Ying 504a08f82d0SHuang Ying return erst_errno(val); 505a08f82d0SHuang Ying } 506a08f82d0SHuang Ying 507a08f82d0SHuang Ying static int __erst_read_from_storage(u64 record_id, u64 offset) 508a08f82d0SHuang Ying { 509a08f82d0SHuang Ying struct apei_exec_context ctx; 510a08f82d0SHuang Ying u64 timeout = FIRMWARE_TIMEOUT; 511a08f82d0SHuang Ying u64 val; 512a08f82d0SHuang Ying int rc; 513a08f82d0SHuang Ying 514a08f82d0SHuang Ying erst_exec_ctx_init(&ctx); 515a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_READ); 516a08f82d0SHuang Ying if (rc) 517a08f82d0SHuang Ying return rc; 518a08f82d0SHuang Ying apei_exec_ctx_set_input(&ctx, offset); 519a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET); 520a08f82d0SHuang Ying if (rc) 521a08f82d0SHuang Ying return rc; 522a08f82d0SHuang Ying apei_exec_ctx_set_input(&ctx, record_id); 523a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID); 524a08f82d0SHuang Ying if (rc) 525a08f82d0SHuang Ying return rc; 526a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION); 527a08f82d0SHuang Ying if (rc) 528a08f82d0SHuang Ying return rc; 529a08f82d0SHuang Ying for (;;) { 530a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS); 531a08f82d0SHuang Ying if (rc) 532a08f82d0SHuang Ying return rc; 533a08f82d0SHuang Ying val = apei_exec_ctx_get_output(&ctx); 534a08f82d0SHuang Ying if (!val) 535a08f82d0SHuang Ying break; 536a08f82d0SHuang Ying if (erst_timedout(&timeout, SPIN_UNIT)) 537a08f82d0SHuang Ying return -EIO; 538a08f82d0SHuang Ying }; 539a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS); 540a08f82d0SHuang Ying if (rc) 541a08f82d0SHuang Ying return rc; 542a08f82d0SHuang Ying val = apei_exec_ctx_get_output(&ctx); 543a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_END); 544a08f82d0SHuang Ying if (rc) 545a08f82d0SHuang Ying return rc; 546a08f82d0SHuang Ying 547a08f82d0SHuang Ying return erst_errno(val); 548a08f82d0SHuang Ying } 549a08f82d0SHuang Ying 550a08f82d0SHuang Ying static int __erst_clear_from_storage(u64 record_id) 551a08f82d0SHuang Ying { 552a08f82d0SHuang Ying struct apei_exec_context ctx; 553a08f82d0SHuang Ying u64 timeout = FIRMWARE_TIMEOUT; 554a08f82d0SHuang Ying u64 val; 555a08f82d0SHuang Ying int rc; 556a08f82d0SHuang Ying 557a08f82d0SHuang Ying erst_exec_ctx_init(&ctx); 558a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_CLEAR); 559a08f82d0SHuang Ying if (rc) 560a08f82d0SHuang Ying return rc; 561a08f82d0SHuang Ying apei_exec_ctx_set_input(&ctx, record_id); 562a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID); 563a08f82d0SHuang Ying if (rc) 564a08f82d0SHuang Ying return rc; 565a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION); 566a08f82d0SHuang Ying if (rc) 567a08f82d0SHuang Ying return rc; 568a08f82d0SHuang Ying for (;;) { 569a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS); 570a08f82d0SHuang Ying if (rc) 571a08f82d0SHuang Ying return rc; 572a08f82d0SHuang Ying val = apei_exec_ctx_get_output(&ctx); 573a08f82d0SHuang Ying if (!val) 574a08f82d0SHuang Ying break; 575a08f82d0SHuang Ying if (erst_timedout(&timeout, SPIN_UNIT)) 576a08f82d0SHuang Ying return -EIO; 577a08f82d0SHuang Ying } 578a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS); 579a08f82d0SHuang Ying if (rc) 580a08f82d0SHuang Ying return rc; 581a08f82d0SHuang Ying val = apei_exec_ctx_get_output(&ctx); 582a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_END); 583a08f82d0SHuang Ying if (rc) 584a08f82d0SHuang Ying return rc; 585a08f82d0SHuang Ying 586a08f82d0SHuang Ying return erst_errno(val); 587a08f82d0SHuang Ying } 588a08f82d0SHuang Ying 589a08f82d0SHuang Ying /* NVRAM ERST Error Log Address Range is not supported yet */ 590a08f82d0SHuang Ying static void pr_unimpl_nvram(void) 591a08f82d0SHuang Ying { 592a08f82d0SHuang Ying if (printk_ratelimit()) 593a08f82d0SHuang Ying pr_warning(ERST_PFX 594a08f82d0SHuang Ying "NVRAM ERST Log Address Range is not implemented yet\n"); 595a08f82d0SHuang Ying } 596a08f82d0SHuang Ying 597a08f82d0SHuang Ying static int __erst_write_to_nvram(const struct cper_record_header *record) 598a08f82d0SHuang Ying { 599a08f82d0SHuang Ying /* do not print message, because printk is not safe for NMI */ 600a08f82d0SHuang Ying return -ENOSYS; 601a08f82d0SHuang Ying } 602a08f82d0SHuang Ying 603a08f82d0SHuang Ying static int __erst_read_to_erange_from_nvram(u64 record_id, u64 *offset) 604a08f82d0SHuang Ying { 605a08f82d0SHuang Ying pr_unimpl_nvram(); 606a08f82d0SHuang Ying return -ENOSYS; 607a08f82d0SHuang Ying } 608a08f82d0SHuang Ying 609a08f82d0SHuang Ying static int __erst_clear_from_nvram(u64 record_id) 610a08f82d0SHuang Ying { 611a08f82d0SHuang Ying pr_unimpl_nvram(); 612a08f82d0SHuang Ying return -ENOSYS; 613a08f82d0SHuang Ying } 614a08f82d0SHuang Ying 615a08f82d0SHuang Ying int erst_write(const struct cper_record_header *record) 616a08f82d0SHuang Ying { 617a08f82d0SHuang Ying int rc; 618a08f82d0SHuang Ying unsigned long flags; 619a08f82d0SHuang Ying struct cper_record_header *rcd_erange; 620a08f82d0SHuang Ying 621a08f82d0SHuang Ying if (erst_disable) 622a08f82d0SHuang Ying return -ENODEV; 623a08f82d0SHuang Ying 624a08f82d0SHuang Ying if (memcmp(record->signature, CPER_SIG_RECORD, CPER_SIG_SIZE)) 625a08f82d0SHuang Ying return -EINVAL; 626a08f82d0SHuang Ying 627a08f82d0SHuang Ying if (erst_erange.attr & ERST_RANGE_NVRAM) { 6283b38bb5fSHuang Ying if (!raw_spin_trylock_irqsave(&erst_lock, flags)) 629a08f82d0SHuang Ying return -EBUSY; 630a08f82d0SHuang Ying rc = __erst_write_to_nvram(record); 6313b38bb5fSHuang Ying raw_spin_unlock_irqrestore(&erst_lock, flags); 632a08f82d0SHuang Ying return rc; 633a08f82d0SHuang Ying } 634a08f82d0SHuang Ying 635a08f82d0SHuang Ying if (record->record_length > erst_erange.size) 636a08f82d0SHuang Ying return -EINVAL; 637a08f82d0SHuang Ying 6383b38bb5fSHuang Ying if (!raw_spin_trylock_irqsave(&erst_lock, flags)) 639a08f82d0SHuang Ying return -EBUSY; 640a08f82d0SHuang Ying memcpy(erst_erange.vaddr, record, record->record_length); 641a08f82d0SHuang Ying rcd_erange = erst_erange.vaddr; 642a08f82d0SHuang Ying /* signature for serialization system */ 643a08f82d0SHuang Ying memcpy(&rcd_erange->persistence_information, "ER", 2); 644a08f82d0SHuang Ying 645a08f82d0SHuang Ying rc = __erst_write_to_storage(0); 6463b38bb5fSHuang Ying raw_spin_unlock_irqrestore(&erst_lock, flags); 647a08f82d0SHuang Ying 648a08f82d0SHuang Ying return rc; 649a08f82d0SHuang Ying } 650a08f82d0SHuang Ying EXPORT_SYMBOL_GPL(erst_write); 651a08f82d0SHuang Ying 652a08f82d0SHuang Ying static int __erst_read_to_erange(u64 record_id, u64 *offset) 653a08f82d0SHuang Ying { 654a08f82d0SHuang Ying int rc; 655a08f82d0SHuang Ying 656a08f82d0SHuang Ying if (erst_erange.attr & ERST_RANGE_NVRAM) 657a08f82d0SHuang Ying return __erst_read_to_erange_from_nvram( 658a08f82d0SHuang Ying record_id, offset); 659a08f82d0SHuang Ying 660a08f82d0SHuang Ying rc = __erst_read_from_storage(record_id, 0); 661a08f82d0SHuang Ying if (rc) 662a08f82d0SHuang Ying return rc; 663a08f82d0SHuang Ying *offset = 0; 664a08f82d0SHuang Ying 665a08f82d0SHuang Ying return 0; 666a08f82d0SHuang Ying } 667a08f82d0SHuang Ying 668a08f82d0SHuang Ying static ssize_t __erst_read(u64 record_id, struct cper_record_header *record, 669a08f82d0SHuang Ying size_t buflen) 670a08f82d0SHuang Ying { 671a08f82d0SHuang Ying int rc; 672a08f82d0SHuang Ying u64 offset, len = 0; 673a08f82d0SHuang Ying struct cper_record_header *rcd_tmp; 674a08f82d0SHuang Ying 675a08f82d0SHuang Ying rc = __erst_read_to_erange(record_id, &offset); 676a08f82d0SHuang Ying if (rc) 677a08f82d0SHuang Ying return rc; 678a08f82d0SHuang Ying rcd_tmp = erst_erange.vaddr + offset; 679a08f82d0SHuang Ying len = rcd_tmp->record_length; 680a08f82d0SHuang Ying if (len <= buflen) 681a08f82d0SHuang Ying memcpy(record, rcd_tmp, len); 682a08f82d0SHuang Ying 683a08f82d0SHuang Ying return len; 684a08f82d0SHuang Ying } 685a08f82d0SHuang Ying 686a08f82d0SHuang Ying /* 687a08f82d0SHuang Ying * If return value > buflen, the buffer size is not big enough, 688a08f82d0SHuang Ying * else if return value < 0, something goes wrong, 689a08f82d0SHuang Ying * else everything is OK, and return value is record length 690a08f82d0SHuang Ying */ 691a08f82d0SHuang Ying ssize_t erst_read(u64 record_id, struct cper_record_header *record, 692a08f82d0SHuang Ying size_t buflen) 693a08f82d0SHuang Ying { 694a08f82d0SHuang Ying ssize_t len; 695a08f82d0SHuang Ying unsigned long flags; 696a08f82d0SHuang Ying 697a08f82d0SHuang Ying if (erst_disable) 698a08f82d0SHuang Ying return -ENODEV; 699a08f82d0SHuang Ying 7003b38bb5fSHuang Ying raw_spin_lock_irqsave(&erst_lock, flags); 701a08f82d0SHuang Ying len = __erst_read(record_id, record, buflen); 7023b38bb5fSHuang Ying raw_spin_unlock_irqrestore(&erst_lock, flags); 703a08f82d0SHuang Ying return len; 704a08f82d0SHuang Ying } 705a08f82d0SHuang Ying EXPORT_SYMBOL_GPL(erst_read); 706a08f82d0SHuang Ying 707a08f82d0SHuang Ying /* 708a08f82d0SHuang Ying * If return value > buflen, the buffer size is not big enough, 709a08f82d0SHuang Ying * else if return value = 0, there is no more record to read, 710a08f82d0SHuang Ying * else if return value < 0, something goes wrong, 711a08f82d0SHuang Ying * else everything is OK, and return value is record length 712a08f82d0SHuang Ying */ 713a08f82d0SHuang Ying ssize_t erst_read_next(struct cper_record_header *record, size_t buflen) 714a08f82d0SHuang Ying { 715a08f82d0SHuang Ying int rc; 716a08f82d0SHuang Ying ssize_t len; 717a08f82d0SHuang Ying unsigned long flags; 718a08f82d0SHuang Ying u64 record_id; 719a08f82d0SHuang Ying 720a08f82d0SHuang Ying if (erst_disable) 721a08f82d0SHuang Ying return -ENODEV; 722a08f82d0SHuang Ying 7233b38bb5fSHuang Ying raw_spin_lock_irqsave(&erst_lock, flags); 724a08f82d0SHuang Ying rc = __erst_get_next_record_id(&record_id); 725a08f82d0SHuang Ying if (rc) { 7263b38bb5fSHuang Ying raw_spin_unlock_irqrestore(&erst_lock, flags); 727a08f82d0SHuang Ying return rc; 728a08f82d0SHuang Ying } 729a08f82d0SHuang Ying /* no more record */ 730a08f82d0SHuang Ying if (record_id == APEI_ERST_INVALID_RECORD_ID) { 7313b38bb5fSHuang Ying raw_spin_unlock_irqrestore(&erst_lock, flags); 732a08f82d0SHuang Ying return 0; 733a08f82d0SHuang Ying } 734a08f82d0SHuang Ying 735a08f82d0SHuang Ying len = __erst_read(record_id, record, buflen); 7363b38bb5fSHuang Ying raw_spin_unlock_irqrestore(&erst_lock, flags); 737a08f82d0SHuang Ying 738a08f82d0SHuang Ying return len; 739a08f82d0SHuang Ying } 740a08f82d0SHuang Ying EXPORT_SYMBOL_GPL(erst_read_next); 741a08f82d0SHuang Ying 742a08f82d0SHuang Ying int erst_clear(u64 record_id) 743a08f82d0SHuang Ying { 744a08f82d0SHuang Ying int rc; 745a08f82d0SHuang Ying unsigned long flags; 746a08f82d0SHuang Ying 747a08f82d0SHuang Ying if (erst_disable) 748a08f82d0SHuang Ying return -ENODEV; 749a08f82d0SHuang Ying 7503b38bb5fSHuang Ying raw_spin_lock_irqsave(&erst_lock, flags); 751a08f82d0SHuang Ying if (erst_erange.attr & ERST_RANGE_NVRAM) 752a08f82d0SHuang Ying rc = __erst_clear_from_nvram(record_id); 753a08f82d0SHuang Ying else 754a08f82d0SHuang Ying rc = __erst_clear_from_storage(record_id); 7553b38bb5fSHuang Ying raw_spin_unlock_irqrestore(&erst_lock, flags); 756a08f82d0SHuang Ying 757a08f82d0SHuang Ying return rc; 758a08f82d0SHuang Ying } 759a08f82d0SHuang Ying EXPORT_SYMBOL_GPL(erst_clear); 760a08f82d0SHuang Ying 761a08f82d0SHuang Ying static int __init setup_erst_disable(char *str) 762a08f82d0SHuang Ying { 763a08f82d0SHuang Ying erst_disable = 1; 764a08f82d0SHuang Ying return 0; 765a08f82d0SHuang Ying } 766a08f82d0SHuang Ying 767a08f82d0SHuang Ying __setup("erst_disable", setup_erst_disable); 768a08f82d0SHuang Ying 769a08f82d0SHuang Ying static int erst_check_table(struct acpi_table_erst *erst_tab) 770a08f82d0SHuang Ying { 7713a78f965SHuang Ying if ((erst_tab->header_length != 7723a78f965SHuang Ying (sizeof(struct acpi_table_erst) - sizeof(erst_tab->header))) 7733a78f965SHuang Ying && (erst_tab->header_length != sizeof(struct acpi_table_einj))) 774a08f82d0SHuang Ying return -EINVAL; 775a08f82d0SHuang Ying if (erst_tab->header.length < sizeof(struct acpi_table_erst)) 776a08f82d0SHuang Ying return -EINVAL; 777a08f82d0SHuang Ying if (erst_tab->entries != 778a08f82d0SHuang Ying (erst_tab->header.length - sizeof(struct acpi_table_erst)) / 779a08f82d0SHuang Ying sizeof(struct acpi_erst_entry)) 780a08f82d0SHuang Ying return -EINVAL; 781a08f82d0SHuang Ying 782a08f82d0SHuang Ying return 0; 783a08f82d0SHuang Ying } 784a08f82d0SHuang Ying 7850bb77c46STony Luck static size_t erst_reader(u64 *id, enum pstore_type_id *type, 7860bb77c46STony Luck struct timespec *time); 7870bb77c46STony Luck static u64 erst_writer(enum pstore_type_id type, size_t size); 7880bb77c46STony Luck 7890bb77c46STony Luck static struct pstore_info erst_info = { 7900bb77c46STony Luck .owner = THIS_MODULE, 7910bb77c46STony Luck .name = "erst", 7920bb77c46STony Luck .read = erst_reader, 7930bb77c46STony Luck .write = erst_writer, 7940bb77c46STony Luck .erase = erst_clear 7950bb77c46STony Luck }; 7960bb77c46STony Luck 7970bb77c46STony Luck #define CPER_CREATOR_PSTORE \ 7980bb77c46STony Luck UUID_LE(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c, \ 7990bb77c46STony Luck 0x64, 0x90, 0xb8, 0x9d) 8000bb77c46STony Luck #define CPER_SECTION_TYPE_DMESG \ 8010bb77c46STony Luck UUID_LE(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54, \ 8020bb77c46STony Luck 0x94, 0x19, 0xeb, 0x12) 8030bb77c46STony Luck #define CPER_SECTION_TYPE_MCE \ 8040bb77c46STony Luck UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96, \ 8050bb77c46STony Luck 0x04, 0x4a, 0x38, 0xfc) 8060bb77c46STony Luck 8070bb77c46STony Luck struct cper_pstore_record { 8080bb77c46STony Luck struct cper_record_header hdr; 8090bb77c46STony Luck struct cper_section_descriptor sec_hdr; 8100bb77c46STony Luck char data[]; 8110bb77c46STony Luck } __packed; 8120bb77c46STony Luck 8130bb77c46STony Luck static size_t erst_reader(u64 *id, enum pstore_type_id *type, 8140bb77c46STony Luck struct timespec *time) 8150bb77c46STony Luck { 8160bb77c46STony Luck int rc; 8170bb77c46STony Luck ssize_t len; 8180bb77c46STony Luck unsigned long flags; 8190bb77c46STony Luck u64 record_id; 8200bb77c46STony Luck struct cper_pstore_record *rcd = (struct cper_pstore_record *) 8210bb77c46STony Luck (erst_info.buf - sizeof(*rcd)); 8220bb77c46STony Luck 8230bb77c46STony Luck if (erst_disable) 8240bb77c46STony Luck return -ENODEV; 8250bb77c46STony Luck 8260bb77c46STony Luck raw_spin_lock_irqsave(&erst_lock, flags); 8270bb77c46STony Luck skip: 8280bb77c46STony Luck rc = __erst_get_next_record_id(&record_id); 8290bb77c46STony Luck if (rc) { 8300bb77c46STony Luck raw_spin_unlock_irqrestore(&erst_lock, flags); 8310bb77c46STony Luck return rc; 8320bb77c46STony Luck } 8330bb77c46STony Luck /* no more record */ 8340bb77c46STony Luck if (record_id == APEI_ERST_INVALID_RECORD_ID) { 8350bb77c46STony Luck raw_spin_unlock_irqrestore(&erst_lock, flags); 8360bb77c46STony Luck return 0; 8370bb77c46STony Luck } 8380bb77c46STony Luck 8390bb77c46STony Luck len = __erst_read(record_id, &rcd->hdr, sizeof(*rcd) + 8400bb77c46STony Luck erst_erange.size); 8410bb77c46STony Luck if (uuid_le_cmp(rcd->hdr.creator_id, CPER_CREATOR_PSTORE) != 0) 8420bb77c46STony Luck goto skip; 8430bb77c46STony Luck raw_spin_unlock_irqrestore(&erst_lock, flags); 8440bb77c46STony Luck 8450bb77c46STony Luck *id = record_id; 8460bb77c46STony Luck if (uuid_le_cmp(rcd->sec_hdr.section_type, 8470bb77c46STony Luck CPER_SECTION_TYPE_DMESG) == 0) 8480bb77c46STony Luck *type = PSTORE_TYPE_DMESG; 8490bb77c46STony Luck else if (uuid_le_cmp(rcd->sec_hdr.section_type, 8500bb77c46STony Luck CPER_SECTION_TYPE_MCE) == 0) 8510bb77c46STony Luck *type = PSTORE_TYPE_MCE; 8520bb77c46STony Luck else 8530bb77c46STony Luck *type = PSTORE_TYPE_UNKNOWN; 8540bb77c46STony Luck 8550bb77c46STony Luck if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP) 8560bb77c46STony Luck time->tv_sec = rcd->hdr.timestamp; 8570bb77c46STony Luck else 8580bb77c46STony Luck time->tv_sec = 0; 8590bb77c46STony Luck time->tv_nsec = 0; 8600bb77c46STony Luck 8610bb77c46STony Luck return len - sizeof(*rcd); 8620bb77c46STony Luck } 8630bb77c46STony Luck 8640bb77c46STony Luck static u64 erst_writer(enum pstore_type_id type, size_t size) 8650bb77c46STony Luck { 8660bb77c46STony Luck struct cper_pstore_record *rcd = (struct cper_pstore_record *) 8670bb77c46STony Luck (erst_info.buf - sizeof(*rcd)); 8680bb77c46STony Luck 8690bb77c46STony Luck memset(rcd, 0, sizeof(*rcd)); 8700bb77c46STony Luck memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE); 8710bb77c46STony Luck rcd->hdr.revision = CPER_RECORD_REV; 8720bb77c46STony Luck rcd->hdr.signature_end = CPER_SIG_END; 8730bb77c46STony Luck rcd->hdr.section_count = 1; 8740bb77c46STony Luck rcd->hdr.error_severity = CPER_SEV_FATAL; 8750bb77c46STony Luck /* timestamp valid. platform_id, partition_id are invalid */ 8760bb77c46STony Luck rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP; 8770bb77c46STony Luck rcd->hdr.timestamp = get_seconds(); 8780bb77c46STony Luck rcd->hdr.record_length = sizeof(*rcd) + size; 8790bb77c46STony Luck rcd->hdr.creator_id = CPER_CREATOR_PSTORE; 8800bb77c46STony Luck rcd->hdr.notification_type = CPER_NOTIFY_MCE; 8810bb77c46STony Luck rcd->hdr.record_id = cper_next_record_id(); 8820bb77c46STony Luck rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR; 8830bb77c46STony Luck 8840bb77c46STony Luck rcd->sec_hdr.section_offset = sizeof(*rcd); 8850bb77c46STony Luck rcd->sec_hdr.section_length = size; 8860bb77c46STony Luck rcd->sec_hdr.revision = CPER_SEC_REV; 8870bb77c46STony Luck /* fru_id and fru_text is invalid */ 8880bb77c46STony Luck rcd->sec_hdr.validation_bits = 0; 8890bb77c46STony Luck rcd->sec_hdr.flags = CPER_SEC_PRIMARY; 8900bb77c46STony Luck switch (type) { 8910bb77c46STony Luck case PSTORE_TYPE_DMESG: 8920bb77c46STony Luck rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG; 8930bb77c46STony Luck break; 8940bb77c46STony Luck case PSTORE_TYPE_MCE: 8950bb77c46STony Luck rcd->sec_hdr.section_type = CPER_SECTION_TYPE_MCE; 8960bb77c46STony Luck break; 8970bb77c46STony Luck default: 8980bb77c46STony Luck return -EINVAL; 8990bb77c46STony Luck } 9000bb77c46STony Luck rcd->sec_hdr.section_severity = CPER_SEV_FATAL; 9010bb77c46STony Luck 9020bb77c46STony Luck erst_write(&rcd->hdr); 9030bb77c46STony Luck 9040bb77c46STony Luck return rcd->hdr.record_id; 9050bb77c46STony Luck } 9060bb77c46STony Luck 907a08f82d0SHuang Ying static int __init erst_init(void) 908a08f82d0SHuang Ying { 909a08f82d0SHuang Ying int rc = 0; 910a08f82d0SHuang Ying acpi_status status; 911a08f82d0SHuang Ying struct apei_exec_context ctx; 912a08f82d0SHuang Ying struct apei_resources erst_resources; 913a08f82d0SHuang Ying struct resource *r; 9140bb77c46STony Luck char *buf; 915a08f82d0SHuang Ying 916a08f82d0SHuang Ying if (acpi_disabled) 917a08f82d0SHuang Ying goto err; 918a08f82d0SHuang Ying 919a08f82d0SHuang Ying if (erst_disable) { 920a08f82d0SHuang Ying pr_info(ERST_PFX 921a08f82d0SHuang Ying "Error Record Serialization Table (ERST) support is disabled.\n"); 922a08f82d0SHuang Ying goto err; 923a08f82d0SHuang Ying } 924a08f82d0SHuang Ying 925a08f82d0SHuang Ying status = acpi_get_table(ACPI_SIG_ERST, 0, 926a08f82d0SHuang Ying (struct acpi_table_header **)&erst_tab); 927a08f82d0SHuang Ying if (status == AE_NOT_FOUND) { 928980533b0SDaniel J Blueman pr_info(ERST_PFX "Table is not found!\n"); 929a08f82d0SHuang Ying goto err; 930a08f82d0SHuang Ying } else if (ACPI_FAILURE(status)) { 931a08f82d0SHuang Ying const char *msg = acpi_format_exception(status); 932a08f82d0SHuang Ying pr_err(ERST_PFX "Failed to get table, %s\n", msg); 933a08f82d0SHuang Ying rc = -EINVAL; 934a08f82d0SHuang Ying goto err; 935a08f82d0SHuang Ying } 936a08f82d0SHuang Ying 937a08f82d0SHuang Ying rc = erst_check_table(erst_tab); 938a08f82d0SHuang Ying if (rc) { 939a08f82d0SHuang Ying pr_err(FW_BUG ERST_PFX "ERST table is invalid\n"); 940a08f82d0SHuang Ying goto err; 941a08f82d0SHuang Ying } 942a08f82d0SHuang Ying 943a08f82d0SHuang Ying apei_resources_init(&erst_resources); 944a08f82d0SHuang Ying erst_exec_ctx_init(&ctx); 945a08f82d0SHuang Ying rc = apei_exec_collect_resources(&ctx, &erst_resources); 946a08f82d0SHuang Ying if (rc) 947a08f82d0SHuang Ying goto err_fini; 948a08f82d0SHuang Ying rc = apei_resources_request(&erst_resources, "APEI ERST"); 949a08f82d0SHuang Ying if (rc) 950a08f82d0SHuang Ying goto err_fini; 951a08f82d0SHuang Ying rc = apei_exec_pre_map_gars(&ctx); 952a08f82d0SHuang Ying if (rc) 953a08f82d0SHuang Ying goto err_release; 954a08f82d0SHuang Ying rc = erst_get_erange(&erst_erange); 955a08f82d0SHuang Ying if (rc) { 956a08f82d0SHuang Ying if (rc == -ENODEV) 957a08f82d0SHuang Ying pr_info(ERST_PFX 958a08f82d0SHuang Ying "The corresponding hardware device or firmware implementation " 959a08f82d0SHuang Ying "is not available.\n"); 960a08f82d0SHuang Ying else 961a08f82d0SHuang Ying pr_err(ERST_PFX 962a08f82d0SHuang Ying "Failed to get Error Log Address Range.\n"); 963a08f82d0SHuang Ying goto err_unmap_reg; 964a08f82d0SHuang Ying } 965a08f82d0SHuang Ying 966a08f82d0SHuang Ying r = request_mem_region(erst_erange.base, erst_erange.size, "APEI ERST"); 967a08f82d0SHuang Ying if (!r) { 968a08f82d0SHuang Ying pr_err(ERST_PFX 969a08f82d0SHuang Ying "Can not request iomem region <0x%16llx-0x%16llx> for ERST.\n", 970a08f82d0SHuang Ying (unsigned long long)erst_erange.base, 971a08f82d0SHuang Ying (unsigned long long)erst_erange.base + erst_erange.size); 972a08f82d0SHuang Ying rc = -EIO; 973a08f82d0SHuang Ying goto err_unmap_reg; 974a08f82d0SHuang Ying } 975a08f82d0SHuang Ying rc = -ENOMEM; 976a08f82d0SHuang Ying erst_erange.vaddr = ioremap_cache(erst_erange.base, 977a08f82d0SHuang Ying erst_erange.size); 978a08f82d0SHuang Ying if (!erst_erange.vaddr) 979a08f82d0SHuang Ying goto err_release_erange; 980a08f82d0SHuang Ying 9810bb77c46STony Luck buf = kmalloc(erst_erange.size, GFP_KERNEL); 9820bb77c46STony Luck mutex_init(&erst_info.buf_mutex); 9830bb77c46STony Luck if (buf) { 9840bb77c46STony Luck erst_info.buf = buf + sizeof(struct cper_pstore_record); 9850bb77c46STony Luck erst_info.bufsize = erst_erange.size - 9860bb77c46STony Luck sizeof(struct cper_pstore_record); 9870bb77c46STony Luck if (pstore_register(&erst_info)) { 9880bb77c46STony Luck pr_info(ERST_PFX "Could not register with persistent store\n"); 9890bb77c46STony Luck kfree(buf); 9900bb77c46STony Luck } 9910bb77c46STony Luck } 9920bb77c46STony Luck 993a08f82d0SHuang Ying pr_info(ERST_PFX 994a08f82d0SHuang Ying "Error Record Serialization Table (ERST) support is initialized.\n"); 995a08f82d0SHuang Ying 996a08f82d0SHuang Ying return 0; 997a08f82d0SHuang Ying 998a08f82d0SHuang Ying err_release_erange: 999a08f82d0SHuang Ying release_mem_region(erst_erange.base, erst_erange.size); 1000a08f82d0SHuang Ying err_unmap_reg: 1001a08f82d0SHuang Ying apei_exec_post_unmap_gars(&ctx); 1002a08f82d0SHuang Ying err_release: 1003a08f82d0SHuang Ying apei_resources_release(&erst_resources); 1004a08f82d0SHuang Ying err_fini: 1005a08f82d0SHuang Ying apei_resources_fini(&erst_resources); 1006a08f82d0SHuang Ying err: 1007a08f82d0SHuang Ying erst_disable = 1; 1008a08f82d0SHuang Ying return rc; 1009a08f82d0SHuang Ying } 1010a08f82d0SHuang Ying 1011a08f82d0SHuang Ying device_initcall(erst_init); 1012