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 */ 57e8a8b252SStefan Weil /* Firmware should respond within 1 milliseconds */ 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 433885b976fSHuang Ying #define ERST_RECORD_ID_CACHE_SIZE_MIN 16 434885b976fSHuang Ying #define ERST_RECORD_ID_CACHE_SIZE_MAX 1024 435885b976fSHuang Ying 436885b976fSHuang Ying struct erst_record_id_cache { 437885b976fSHuang Ying struct mutex lock; 438885b976fSHuang Ying u64 *entries; 439885b976fSHuang Ying int len; 440885b976fSHuang Ying int size; 441885b976fSHuang Ying int refcount; 442885b976fSHuang Ying }; 443885b976fSHuang Ying 444885b976fSHuang Ying static struct erst_record_id_cache erst_record_id_cache = { 445885b976fSHuang Ying .lock = __MUTEX_INITIALIZER(erst_record_id_cache.lock), 446885b976fSHuang Ying .refcount = 0, 447885b976fSHuang Ying }; 448885b976fSHuang Ying 449a08f82d0SHuang Ying static int __erst_get_next_record_id(u64 *record_id) 450a08f82d0SHuang Ying { 451a08f82d0SHuang Ying struct apei_exec_context ctx; 452a08f82d0SHuang Ying int rc; 453a08f82d0SHuang Ying 454a08f82d0SHuang Ying erst_exec_ctx_init(&ctx); 455a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_ID); 456a08f82d0SHuang Ying if (rc) 457a08f82d0SHuang Ying return rc; 458a08f82d0SHuang Ying *record_id = apei_exec_ctx_get_output(&ctx); 459a08f82d0SHuang Ying 460a08f82d0SHuang Ying return 0; 461a08f82d0SHuang Ying } 462a08f82d0SHuang Ying 463885b976fSHuang Ying int erst_get_record_id_begin(int *pos) 464885b976fSHuang Ying { 465885b976fSHuang Ying int rc; 466885b976fSHuang Ying 467885b976fSHuang Ying if (erst_disable) 468885b976fSHuang Ying return -ENODEV; 469885b976fSHuang Ying 470885b976fSHuang Ying rc = mutex_lock_interruptible(&erst_record_id_cache.lock); 471885b976fSHuang Ying if (rc) 472885b976fSHuang Ying return rc; 473885b976fSHuang Ying erst_record_id_cache.refcount++; 474885b976fSHuang Ying mutex_unlock(&erst_record_id_cache.lock); 475885b976fSHuang Ying 476885b976fSHuang Ying *pos = 0; 477885b976fSHuang Ying 478885b976fSHuang Ying return 0; 479885b976fSHuang Ying } 480885b976fSHuang Ying EXPORT_SYMBOL_GPL(erst_get_record_id_begin); 481885b976fSHuang Ying 482885b976fSHuang Ying /* erst_record_id_cache.lock must be held by caller */ 483885b976fSHuang Ying static int __erst_record_id_cache_add_one(void) 484885b976fSHuang Ying { 485885b976fSHuang Ying u64 id, prev_id, first_id; 486885b976fSHuang Ying int i, rc; 487885b976fSHuang Ying u64 *entries; 488885b976fSHuang Ying unsigned long flags; 489885b976fSHuang Ying 490885b976fSHuang Ying id = prev_id = first_id = APEI_ERST_INVALID_RECORD_ID; 491885b976fSHuang Ying retry: 492885b976fSHuang Ying raw_spin_lock_irqsave(&erst_lock, flags); 493885b976fSHuang Ying rc = __erst_get_next_record_id(&id); 494885b976fSHuang Ying raw_spin_unlock_irqrestore(&erst_lock, flags); 495885b976fSHuang Ying if (rc == -ENOENT) 496885b976fSHuang Ying return 0; 497885b976fSHuang Ying if (rc) 498885b976fSHuang Ying return rc; 499885b976fSHuang Ying if (id == APEI_ERST_INVALID_RECORD_ID) 500885b976fSHuang Ying return 0; 501885b976fSHuang Ying /* can not skip current ID, or loop back to first ID */ 502885b976fSHuang Ying if (id == prev_id || id == first_id) 503885b976fSHuang Ying return 0; 504885b976fSHuang Ying if (first_id == APEI_ERST_INVALID_RECORD_ID) 505885b976fSHuang Ying first_id = id; 506885b976fSHuang Ying prev_id = id; 507885b976fSHuang Ying 508885b976fSHuang Ying entries = erst_record_id_cache.entries; 509885b976fSHuang Ying for (i = 0; i < erst_record_id_cache.len; i++) { 510885b976fSHuang Ying if (entries[i] == id) 511885b976fSHuang Ying break; 512885b976fSHuang Ying } 513885b976fSHuang Ying /* record id already in cache, try next */ 514885b976fSHuang Ying if (i < erst_record_id_cache.len) 515885b976fSHuang Ying goto retry; 516885b976fSHuang Ying if (erst_record_id_cache.len >= erst_record_id_cache.size) { 517885b976fSHuang Ying int new_size, alloc_size; 518885b976fSHuang Ying u64 *new_entries; 519885b976fSHuang Ying 520885b976fSHuang Ying new_size = erst_record_id_cache.size * 2; 521885b976fSHuang Ying new_size = clamp_val(new_size, ERST_RECORD_ID_CACHE_SIZE_MIN, 522885b976fSHuang Ying ERST_RECORD_ID_CACHE_SIZE_MAX); 523885b976fSHuang Ying if (new_size <= erst_record_id_cache.size) { 524885b976fSHuang Ying if (printk_ratelimit()) 525885b976fSHuang Ying pr_warning(FW_WARN ERST_PFX 526885b976fSHuang Ying "too many record ID!\n"); 527885b976fSHuang Ying return 0; 528885b976fSHuang Ying } 529885b976fSHuang Ying alloc_size = new_size * sizeof(entries[0]); 530885b976fSHuang Ying if (alloc_size < PAGE_SIZE) 531885b976fSHuang Ying new_entries = kmalloc(alloc_size, GFP_KERNEL); 532885b976fSHuang Ying else 533885b976fSHuang Ying new_entries = vmalloc(alloc_size); 534885b976fSHuang Ying if (!new_entries) 535885b976fSHuang Ying return -ENOMEM; 536885b976fSHuang Ying memcpy(new_entries, entries, 537885b976fSHuang Ying erst_record_id_cache.len * sizeof(entries[0])); 538885b976fSHuang Ying if (erst_record_id_cache.size < PAGE_SIZE) 539885b976fSHuang Ying kfree(entries); 540885b976fSHuang Ying else 541885b976fSHuang Ying vfree(entries); 542885b976fSHuang Ying erst_record_id_cache.entries = entries = new_entries; 543885b976fSHuang Ying erst_record_id_cache.size = new_size; 544885b976fSHuang Ying } 545885b976fSHuang Ying entries[i] = id; 546885b976fSHuang Ying erst_record_id_cache.len++; 547885b976fSHuang Ying 548885b976fSHuang Ying return 1; 549885b976fSHuang Ying } 550885b976fSHuang Ying 551a08f82d0SHuang Ying /* 552a08f82d0SHuang Ying * Get the record ID of an existing error record on the persistent 553a08f82d0SHuang Ying * storage. If there is no error record on the persistent storage, the 554a08f82d0SHuang Ying * returned record_id is APEI_ERST_INVALID_RECORD_ID. 555a08f82d0SHuang Ying */ 556885b976fSHuang Ying int erst_get_record_id_next(int *pos, u64 *record_id) 557a08f82d0SHuang Ying { 558885b976fSHuang Ying int rc = 0; 559885b976fSHuang Ying u64 *entries; 560a08f82d0SHuang Ying 561a08f82d0SHuang Ying if (erst_disable) 562a08f82d0SHuang Ying return -ENODEV; 563a08f82d0SHuang Ying 564885b976fSHuang Ying /* must be enclosed by erst_get_record_id_begin/end */ 565885b976fSHuang Ying BUG_ON(!erst_record_id_cache.refcount); 566885b976fSHuang Ying BUG_ON(*pos < 0 || *pos > erst_record_id_cache.len); 567885b976fSHuang Ying 568885b976fSHuang Ying mutex_lock(&erst_record_id_cache.lock); 569885b976fSHuang Ying entries = erst_record_id_cache.entries; 570885b976fSHuang Ying for (; *pos < erst_record_id_cache.len; (*pos)++) 571885b976fSHuang Ying if (entries[*pos] != APEI_ERST_INVALID_RECORD_ID) 572885b976fSHuang Ying break; 573885b976fSHuang Ying /* found next record id in cache */ 574885b976fSHuang Ying if (*pos < erst_record_id_cache.len) { 575885b976fSHuang Ying *record_id = entries[*pos]; 576885b976fSHuang Ying (*pos)++; 577885b976fSHuang Ying goto out_unlock; 578885b976fSHuang Ying } 579885b976fSHuang Ying 580885b976fSHuang Ying /* Try to add one more record ID to cache */ 581885b976fSHuang Ying rc = __erst_record_id_cache_add_one(); 582885b976fSHuang Ying if (rc < 0) 583885b976fSHuang Ying goto out_unlock; 584885b976fSHuang Ying /* successfully add one new ID */ 585885b976fSHuang Ying if (rc == 1) { 586885b976fSHuang Ying *record_id = erst_record_id_cache.entries[*pos]; 587885b976fSHuang Ying (*pos)++; 588885b976fSHuang Ying rc = 0; 589885b976fSHuang Ying } else { 590885b976fSHuang Ying *pos = -1; 591885b976fSHuang Ying *record_id = APEI_ERST_INVALID_RECORD_ID; 592885b976fSHuang Ying } 593885b976fSHuang Ying out_unlock: 594885b976fSHuang Ying mutex_unlock(&erst_record_id_cache.lock); 595a08f82d0SHuang Ying 596a08f82d0SHuang Ying return rc; 597a08f82d0SHuang Ying } 598885b976fSHuang Ying EXPORT_SYMBOL_GPL(erst_get_record_id_next); 599885b976fSHuang Ying 600885b976fSHuang Ying /* erst_record_id_cache.lock must be held by caller */ 601885b976fSHuang Ying static void __erst_record_id_cache_compact(void) 602885b976fSHuang Ying { 603885b976fSHuang Ying int i, wpos = 0; 604885b976fSHuang Ying u64 *entries; 605885b976fSHuang Ying 606885b976fSHuang Ying if (erst_record_id_cache.refcount) 607885b976fSHuang Ying return; 608885b976fSHuang Ying 609885b976fSHuang Ying entries = erst_record_id_cache.entries; 610885b976fSHuang Ying for (i = 0; i < erst_record_id_cache.len; i++) { 611885b976fSHuang Ying if (entries[i] == APEI_ERST_INVALID_RECORD_ID) 612885b976fSHuang Ying continue; 613885b976fSHuang Ying if (wpos != i) 614885b976fSHuang Ying memcpy(&entries[wpos], &entries[i], sizeof(entries[i])); 615885b976fSHuang Ying wpos++; 616885b976fSHuang Ying } 617885b976fSHuang Ying erst_record_id_cache.len = wpos; 618885b976fSHuang Ying } 619885b976fSHuang Ying 620885b976fSHuang Ying void erst_get_record_id_end(void) 621885b976fSHuang Ying { 622885b976fSHuang Ying /* 623885b976fSHuang Ying * erst_disable != 0 should be detected by invoker via the 624885b976fSHuang Ying * return value of erst_get_record_id_begin/next, so this 625885b976fSHuang Ying * function should not be called for erst_disable != 0. 626885b976fSHuang Ying */ 627885b976fSHuang Ying BUG_ON(erst_disable); 628885b976fSHuang Ying 629885b976fSHuang Ying mutex_lock(&erst_record_id_cache.lock); 630885b976fSHuang Ying erst_record_id_cache.refcount--; 631885b976fSHuang Ying BUG_ON(erst_record_id_cache.refcount < 0); 632885b976fSHuang Ying __erst_record_id_cache_compact(); 633885b976fSHuang Ying mutex_unlock(&erst_record_id_cache.lock); 634885b976fSHuang Ying } 635885b976fSHuang Ying EXPORT_SYMBOL_GPL(erst_get_record_id_end); 636a08f82d0SHuang Ying 637a08f82d0SHuang Ying static int __erst_write_to_storage(u64 offset) 638a08f82d0SHuang Ying { 639a08f82d0SHuang Ying struct apei_exec_context ctx; 640a08f82d0SHuang Ying u64 timeout = FIRMWARE_TIMEOUT; 641a08f82d0SHuang Ying u64 val; 642a08f82d0SHuang Ying int rc; 643a08f82d0SHuang Ying 644a08f82d0SHuang Ying erst_exec_ctx_init(&ctx); 645392913deSHuang Ying rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_WRITE); 646a08f82d0SHuang Ying if (rc) 647a08f82d0SHuang Ying return rc; 648a08f82d0SHuang Ying apei_exec_ctx_set_input(&ctx, offset); 649a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET); 650a08f82d0SHuang Ying if (rc) 651a08f82d0SHuang Ying return rc; 652a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION); 653a08f82d0SHuang Ying if (rc) 654a08f82d0SHuang Ying return rc; 655a08f82d0SHuang Ying for (;;) { 656a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS); 657a08f82d0SHuang Ying if (rc) 658a08f82d0SHuang Ying return rc; 659a08f82d0SHuang Ying val = apei_exec_ctx_get_output(&ctx); 660a08f82d0SHuang Ying if (!val) 661a08f82d0SHuang Ying break; 662a08f82d0SHuang Ying if (erst_timedout(&timeout, SPIN_UNIT)) 663a08f82d0SHuang Ying return -EIO; 664a08f82d0SHuang Ying } 665a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS); 666a08f82d0SHuang Ying if (rc) 667a08f82d0SHuang Ying return rc; 668a08f82d0SHuang Ying val = apei_exec_ctx_get_output(&ctx); 669392913deSHuang Ying rc = apei_exec_run_optional(&ctx, ACPI_ERST_END); 670a08f82d0SHuang Ying if (rc) 671a08f82d0SHuang Ying return rc; 672a08f82d0SHuang Ying 673a08f82d0SHuang Ying return erst_errno(val); 674a08f82d0SHuang Ying } 675a08f82d0SHuang Ying 676a08f82d0SHuang Ying static int __erst_read_from_storage(u64 record_id, u64 offset) 677a08f82d0SHuang Ying { 678a08f82d0SHuang Ying struct apei_exec_context ctx; 679a08f82d0SHuang Ying u64 timeout = FIRMWARE_TIMEOUT; 680a08f82d0SHuang Ying u64 val; 681a08f82d0SHuang Ying int rc; 682a08f82d0SHuang Ying 683a08f82d0SHuang Ying erst_exec_ctx_init(&ctx); 684392913deSHuang Ying rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_READ); 685a08f82d0SHuang Ying if (rc) 686a08f82d0SHuang Ying return rc; 687a08f82d0SHuang Ying apei_exec_ctx_set_input(&ctx, offset); 688a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET); 689a08f82d0SHuang Ying if (rc) 690a08f82d0SHuang Ying return rc; 691a08f82d0SHuang Ying apei_exec_ctx_set_input(&ctx, record_id); 692a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID); 693a08f82d0SHuang Ying if (rc) 694a08f82d0SHuang Ying return rc; 695a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION); 696a08f82d0SHuang Ying if (rc) 697a08f82d0SHuang Ying return rc; 698a08f82d0SHuang Ying for (;;) { 699a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS); 700a08f82d0SHuang Ying if (rc) 701a08f82d0SHuang Ying return rc; 702a08f82d0SHuang Ying val = apei_exec_ctx_get_output(&ctx); 703a08f82d0SHuang Ying if (!val) 704a08f82d0SHuang Ying break; 705a08f82d0SHuang Ying if (erst_timedout(&timeout, SPIN_UNIT)) 706a08f82d0SHuang Ying return -EIO; 707a08f82d0SHuang Ying }; 708a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS); 709a08f82d0SHuang Ying if (rc) 710a08f82d0SHuang Ying return rc; 711a08f82d0SHuang Ying val = apei_exec_ctx_get_output(&ctx); 712392913deSHuang Ying rc = apei_exec_run_optional(&ctx, ACPI_ERST_END); 713a08f82d0SHuang Ying if (rc) 714a08f82d0SHuang Ying return rc; 715a08f82d0SHuang Ying 716a08f82d0SHuang Ying return erst_errno(val); 717a08f82d0SHuang Ying } 718a08f82d0SHuang Ying 719a08f82d0SHuang Ying static int __erst_clear_from_storage(u64 record_id) 720a08f82d0SHuang Ying { 721a08f82d0SHuang Ying struct apei_exec_context ctx; 722a08f82d0SHuang Ying u64 timeout = FIRMWARE_TIMEOUT; 723a08f82d0SHuang Ying u64 val; 724a08f82d0SHuang Ying int rc; 725a08f82d0SHuang Ying 726a08f82d0SHuang Ying erst_exec_ctx_init(&ctx); 727392913deSHuang Ying rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_CLEAR); 728a08f82d0SHuang Ying if (rc) 729a08f82d0SHuang Ying return rc; 730a08f82d0SHuang Ying apei_exec_ctx_set_input(&ctx, record_id); 731a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID); 732a08f82d0SHuang Ying if (rc) 733a08f82d0SHuang Ying return rc; 734a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION); 735a08f82d0SHuang Ying if (rc) 736a08f82d0SHuang Ying return rc; 737a08f82d0SHuang Ying for (;;) { 738a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS); 739a08f82d0SHuang Ying if (rc) 740a08f82d0SHuang Ying return rc; 741a08f82d0SHuang Ying val = apei_exec_ctx_get_output(&ctx); 742a08f82d0SHuang Ying if (!val) 743a08f82d0SHuang Ying break; 744a08f82d0SHuang Ying if (erst_timedout(&timeout, SPIN_UNIT)) 745a08f82d0SHuang Ying return -EIO; 746a08f82d0SHuang Ying } 747a08f82d0SHuang Ying rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS); 748a08f82d0SHuang Ying if (rc) 749a08f82d0SHuang Ying return rc; 750a08f82d0SHuang Ying val = apei_exec_ctx_get_output(&ctx); 751392913deSHuang Ying rc = apei_exec_run_optional(&ctx, ACPI_ERST_END); 752a08f82d0SHuang Ying if (rc) 753a08f82d0SHuang Ying return rc; 754a08f82d0SHuang Ying 755a08f82d0SHuang Ying return erst_errno(val); 756a08f82d0SHuang Ying } 757a08f82d0SHuang Ying 758a08f82d0SHuang Ying /* NVRAM ERST Error Log Address Range is not supported yet */ 759a08f82d0SHuang Ying static void pr_unimpl_nvram(void) 760a08f82d0SHuang Ying { 761a08f82d0SHuang Ying if (printk_ratelimit()) 762a08f82d0SHuang Ying pr_warning(ERST_PFX 763a08f82d0SHuang Ying "NVRAM ERST Log Address Range is not implemented yet\n"); 764a08f82d0SHuang Ying } 765a08f82d0SHuang Ying 766a08f82d0SHuang Ying static int __erst_write_to_nvram(const struct cper_record_header *record) 767a08f82d0SHuang Ying { 768a08f82d0SHuang Ying /* do not print message, because printk is not safe for NMI */ 769a08f82d0SHuang Ying return -ENOSYS; 770a08f82d0SHuang Ying } 771a08f82d0SHuang Ying 772a08f82d0SHuang Ying static int __erst_read_to_erange_from_nvram(u64 record_id, u64 *offset) 773a08f82d0SHuang Ying { 774a08f82d0SHuang Ying pr_unimpl_nvram(); 775a08f82d0SHuang Ying return -ENOSYS; 776a08f82d0SHuang Ying } 777a08f82d0SHuang Ying 778a08f82d0SHuang Ying static int __erst_clear_from_nvram(u64 record_id) 779a08f82d0SHuang Ying { 780a08f82d0SHuang Ying pr_unimpl_nvram(); 781a08f82d0SHuang Ying return -ENOSYS; 782a08f82d0SHuang Ying } 783a08f82d0SHuang Ying 784a08f82d0SHuang Ying int erst_write(const struct cper_record_header *record) 785a08f82d0SHuang Ying { 786a08f82d0SHuang Ying int rc; 787a08f82d0SHuang Ying unsigned long flags; 788a08f82d0SHuang Ying struct cper_record_header *rcd_erange; 789a08f82d0SHuang Ying 790a08f82d0SHuang Ying if (erst_disable) 791a08f82d0SHuang Ying return -ENODEV; 792a08f82d0SHuang Ying 793a08f82d0SHuang Ying if (memcmp(record->signature, CPER_SIG_RECORD, CPER_SIG_SIZE)) 794a08f82d0SHuang Ying return -EINVAL; 795a08f82d0SHuang Ying 796a08f82d0SHuang Ying if (erst_erange.attr & ERST_RANGE_NVRAM) { 7973b38bb5fSHuang Ying if (!raw_spin_trylock_irqsave(&erst_lock, flags)) 798a08f82d0SHuang Ying return -EBUSY; 799a08f82d0SHuang Ying rc = __erst_write_to_nvram(record); 8003b38bb5fSHuang Ying raw_spin_unlock_irqrestore(&erst_lock, flags); 801a08f82d0SHuang Ying return rc; 802a08f82d0SHuang Ying } 803a08f82d0SHuang Ying 804a08f82d0SHuang Ying if (record->record_length > erst_erange.size) 805a08f82d0SHuang Ying return -EINVAL; 806a08f82d0SHuang Ying 8073b38bb5fSHuang Ying if (!raw_spin_trylock_irqsave(&erst_lock, flags)) 808a08f82d0SHuang Ying return -EBUSY; 809a08f82d0SHuang Ying memcpy(erst_erange.vaddr, record, record->record_length); 810a08f82d0SHuang Ying rcd_erange = erst_erange.vaddr; 811a08f82d0SHuang Ying /* signature for serialization system */ 812a08f82d0SHuang Ying memcpy(&rcd_erange->persistence_information, "ER", 2); 813a08f82d0SHuang Ying 814a08f82d0SHuang Ying rc = __erst_write_to_storage(0); 8153b38bb5fSHuang Ying raw_spin_unlock_irqrestore(&erst_lock, flags); 816a08f82d0SHuang Ying 817a08f82d0SHuang Ying return rc; 818a08f82d0SHuang Ying } 819a08f82d0SHuang Ying EXPORT_SYMBOL_GPL(erst_write); 820a08f82d0SHuang Ying 821a08f82d0SHuang Ying static int __erst_read_to_erange(u64 record_id, u64 *offset) 822a08f82d0SHuang Ying { 823a08f82d0SHuang Ying int rc; 824a08f82d0SHuang Ying 825a08f82d0SHuang Ying if (erst_erange.attr & ERST_RANGE_NVRAM) 826a08f82d0SHuang Ying return __erst_read_to_erange_from_nvram( 827a08f82d0SHuang Ying record_id, offset); 828a08f82d0SHuang Ying 829a08f82d0SHuang Ying rc = __erst_read_from_storage(record_id, 0); 830a08f82d0SHuang Ying if (rc) 831a08f82d0SHuang Ying return rc; 832a08f82d0SHuang Ying *offset = 0; 833a08f82d0SHuang Ying 834a08f82d0SHuang Ying return 0; 835a08f82d0SHuang Ying } 836a08f82d0SHuang Ying 837a08f82d0SHuang Ying static ssize_t __erst_read(u64 record_id, struct cper_record_header *record, 838a08f82d0SHuang Ying size_t buflen) 839a08f82d0SHuang Ying { 840a08f82d0SHuang Ying int rc; 841a08f82d0SHuang Ying u64 offset, len = 0; 842a08f82d0SHuang Ying struct cper_record_header *rcd_tmp; 843a08f82d0SHuang Ying 844a08f82d0SHuang Ying rc = __erst_read_to_erange(record_id, &offset); 845a08f82d0SHuang Ying if (rc) 846a08f82d0SHuang Ying return rc; 847a08f82d0SHuang Ying rcd_tmp = erst_erange.vaddr + offset; 848a08f82d0SHuang Ying len = rcd_tmp->record_length; 849a08f82d0SHuang Ying if (len <= buflen) 850a08f82d0SHuang Ying memcpy(record, rcd_tmp, len); 851a08f82d0SHuang Ying 852a08f82d0SHuang Ying return len; 853a08f82d0SHuang Ying } 854a08f82d0SHuang Ying 855a08f82d0SHuang Ying /* 856a08f82d0SHuang Ying * If return value > buflen, the buffer size is not big enough, 857a08f82d0SHuang Ying * else if return value < 0, something goes wrong, 858a08f82d0SHuang Ying * else everything is OK, and return value is record length 859a08f82d0SHuang Ying */ 860a08f82d0SHuang Ying ssize_t erst_read(u64 record_id, struct cper_record_header *record, 861a08f82d0SHuang Ying size_t buflen) 862a08f82d0SHuang Ying { 863a08f82d0SHuang Ying ssize_t len; 864a08f82d0SHuang Ying unsigned long flags; 865a08f82d0SHuang Ying 866a08f82d0SHuang Ying if (erst_disable) 867a08f82d0SHuang Ying return -ENODEV; 868a08f82d0SHuang Ying 8693b38bb5fSHuang Ying raw_spin_lock_irqsave(&erst_lock, flags); 870a08f82d0SHuang Ying len = __erst_read(record_id, record, buflen); 8713b38bb5fSHuang Ying raw_spin_unlock_irqrestore(&erst_lock, flags); 872a08f82d0SHuang Ying return len; 873a08f82d0SHuang Ying } 874a08f82d0SHuang Ying EXPORT_SYMBOL_GPL(erst_read); 875a08f82d0SHuang Ying 876a08f82d0SHuang Ying int erst_clear(u64 record_id) 877a08f82d0SHuang Ying { 878885b976fSHuang Ying int rc, i; 879a08f82d0SHuang Ying unsigned long flags; 880885b976fSHuang Ying u64 *entries; 881a08f82d0SHuang Ying 882a08f82d0SHuang Ying if (erst_disable) 883a08f82d0SHuang Ying return -ENODEV; 884a08f82d0SHuang Ying 885885b976fSHuang Ying rc = mutex_lock_interruptible(&erst_record_id_cache.lock); 886885b976fSHuang Ying if (rc) 887885b976fSHuang Ying return rc; 8883b38bb5fSHuang Ying raw_spin_lock_irqsave(&erst_lock, flags); 889a08f82d0SHuang Ying if (erst_erange.attr & ERST_RANGE_NVRAM) 890a08f82d0SHuang Ying rc = __erst_clear_from_nvram(record_id); 891a08f82d0SHuang Ying else 892a08f82d0SHuang Ying rc = __erst_clear_from_storage(record_id); 8933b38bb5fSHuang Ying raw_spin_unlock_irqrestore(&erst_lock, flags); 894885b976fSHuang Ying if (rc) 895885b976fSHuang Ying goto out; 896885b976fSHuang Ying entries = erst_record_id_cache.entries; 897885b976fSHuang Ying for (i = 0; i < erst_record_id_cache.len; i++) { 898885b976fSHuang Ying if (entries[i] == record_id) 899885b976fSHuang Ying entries[i] = APEI_ERST_INVALID_RECORD_ID; 900885b976fSHuang Ying } 901885b976fSHuang Ying __erst_record_id_cache_compact(); 902885b976fSHuang Ying out: 903885b976fSHuang Ying mutex_unlock(&erst_record_id_cache.lock); 904a08f82d0SHuang Ying return rc; 905a08f82d0SHuang Ying } 906a08f82d0SHuang Ying EXPORT_SYMBOL_GPL(erst_clear); 907a08f82d0SHuang Ying 908a08f82d0SHuang Ying static int __init setup_erst_disable(char *str) 909a08f82d0SHuang Ying { 910a08f82d0SHuang Ying erst_disable = 1; 911a08f82d0SHuang Ying return 0; 912a08f82d0SHuang Ying } 913a08f82d0SHuang Ying 914a08f82d0SHuang Ying __setup("erst_disable", setup_erst_disable); 915a08f82d0SHuang Ying 916a08f82d0SHuang Ying static int erst_check_table(struct acpi_table_erst *erst_tab) 917a08f82d0SHuang Ying { 9183a78f965SHuang Ying if ((erst_tab->header_length != 9193a78f965SHuang Ying (sizeof(struct acpi_table_erst) - sizeof(erst_tab->header))) 9203a78f965SHuang Ying && (erst_tab->header_length != sizeof(struct acpi_table_einj))) 921a08f82d0SHuang Ying return -EINVAL; 922a08f82d0SHuang Ying if (erst_tab->header.length < sizeof(struct acpi_table_erst)) 923a08f82d0SHuang Ying return -EINVAL; 924a08f82d0SHuang Ying if (erst_tab->entries != 925a08f82d0SHuang Ying (erst_tab->header.length - sizeof(struct acpi_table_erst)) / 926a08f82d0SHuang Ying sizeof(struct acpi_erst_entry)) 927a08f82d0SHuang Ying return -EINVAL; 928a08f82d0SHuang Ying 929a08f82d0SHuang Ying return 0; 930a08f82d0SHuang Ying } 931a08f82d0SHuang Ying 93206cf91b4SChen Gong static int erst_open_pstore(struct pstore_info *psi); 93306cf91b4SChen Gong static int erst_close_pstore(struct pstore_info *psi); 9348d38d74bSChen Gong static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, 935f6f82851SKees Cook struct timespec *time, char **buf, 936f6f82851SKees Cook struct pstore_info *psi); 937b238b8faSChen Gong static int erst_writer(enum pstore_type_id type, u64 *id, unsigned int part, 938b94fdd07SMatthew Garrett size_t size, struct pstore_info *psi); 93956280682SMatthew Garrett static int erst_clearer(enum pstore_type_id type, u64 id, 94056280682SMatthew Garrett struct pstore_info *psi); 9410bb77c46STony Luck 9420bb77c46STony Luck static struct pstore_info erst_info = { 9430bb77c46STony Luck .owner = THIS_MODULE, 9440bb77c46STony Luck .name = "erst", 94506cf91b4SChen Gong .open = erst_open_pstore, 94606cf91b4SChen Gong .close = erst_close_pstore, 9470bb77c46STony Luck .read = erst_reader, 9480bb77c46STony Luck .write = erst_writer, 949638c1fd3SMatthew Garrett .erase = erst_clearer 9500bb77c46STony Luck }; 9510bb77c46STony Luck 9520bb77c46STony Luck #define CPER_CREATOR_PSTORE \ 9530bb77c46STony Luck UUID_LE(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c, \ 9540bb77c46STony Luck 0x64, 0x90, 0xb8, 0x9d) 9550bb77c46STony Luck #define CPER_SECTION_TYPE_DMESG \ 9560bb77c46STony Luck UUID_LE(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54, \ 9570bb77c46STony Luck 0x94, 0x19, 0xeb, 0x12) 9580bb77c46STony Luck #define CPER_SECTION_TYPE_MCE \ 9590bb77c46STony Luck UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96, \ 9600bb77c46STony Luck 0x04, 0x4a, 0x38, 0xfc) 9610bb77c46STony Luck 9620bb77c46STony Luck struct cper_pstore_record { 9630bb77c46STony Luck struct cper_record_header hdr; 9640bb77c46STony Luck struct cper_section_descriptor sec_hdr; 9650bb77c46STony Luck char data[]; 9660bb77c46STony Luck } __packed; 9670bb77c46STony Luck 96806cf91b4SChen Gong static int reader_pos; 96906cf91b4SChen Gong 97006cf91b4SChen Gong static int erst_open_pstore(struct pstore_info *psi) 97106cf91b4SChen Gong { 97206cf91b4SChen Gong int rc; 97306cf91b4SChen Gong 97406cf91b4SChen Gong if (erst_disable) 97506cf91b4SChen Gong return -ENODEV; 97606cf91b4SChen Gong 97706cf91b4SChen Gong rc = erst_get_record_id_begin(&reader_pos); 97806cf91b4SChen Gong 97906cf91b4SChen Gong return rc; 98006cf91b4SChen Gong } 98106cf91b4SChen Gong 98206cf91b4SChen Gong static int erst_close_pstore(struct pstore_info *psi) 98306cf91b4SChen Gong { 98406cf91b4SChen Gong erst_get_record_id_end(); 98506cf91b4SChen Gong 98606cf91b4SChen Gong return 0; 98706cf91b4SChen Gong } 98806cf91b4SChen Gong 9898d38d74bSChen Gong static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, 990f6f82851SKees Cook struct timespec *time, char **buf, 991f6f82851SKees Cook struct pstore_info *psi) 9920bb77c46STony Luck { 9930bb77c46STony Luck int rc; 99406cf91b4SChen Gong ssize_t len = 0; 9950bb77c46STony Luck u64 record_id; 996f6f82851SKees Cook struct cper_pstore_record *rcd; 997f6f82851SKees Cook size_t rcd_len = sizeof(*rcd) + erst_info.bufsize; 9980bb77c46STony Luck 9990bb77c46STony Luck if (erst_disable) 10000bb77c46STony Luck return -ENODEV; 10010bb77c46STony Luck 1002f6f82851SKees Cook rcd = kmalloc(rcd_len, GFP_KERNEL); 1003f6f82851SKees Cook if (!rcd) { 1004f6f82851SKees Cook rc = -ENOMEM; 1005f6f82851SKees Cook goto out; 1006f6f82851SKees Cook } 10070bb77c46STony Luck skip: 100806cf91b4SChen Gong rc = erst_get_record_id_next(&reader_pos, &record_id); 100906cf91b4SChen Gong if (rc) 101006cf91b4SChen Gong goto out; 101106cf91b4SChen Gong 10120bb77c46STony Luck /* no more record */ 10130bb77c46STony Luck if (record_id == APEI_ERST_INVALID_RECORD_ID) { 1014f6f82851SKees Cook rc = -EINVAL; 101506cf91b4SChen Gong goto out; 10160bb77c46STony Luck } 10170bb77c46STony Luck 1018f6f82851SKees Cook len = erst_read(record_id, &rcd->hdr, rcd_len); 1019f5ec25deSChen Gong /* The record may be cleared by others, try read next record */ 1020f5ec25deSChen Gong if (len == -ENOENT) 1021f5ec25deSChen Gong goto skip; 1022f6f82851SKees Cook else if (len < sizeof(*rcd)) { 1023f6f82851SKees Cook rc = -EIO; 1024f5ec25deSChen Gong goto out; 1025f5ec25deSChen Gong } 10260bb77c46STony Luck if (uuid_le_cmp(rcd->hdr.creator_id, CPER_CREATOR_PSTORE) != 0) 10270bb77c46STony Luck goto skip; 10280bb77c46STony Luck 1029f6f82851SKees Cook *buf = kmalloc(len, GFP_KERNEL); 1030f6f82851SKees Cook if (*buf == NULL) { 1031f6f82851SKees Cook rc = -ENOMEM; 1032f6f82851SKees Cook goto out; 1033f6f82851SKees Cook } 1034f6f82851SKees Cook memcpy(*buf, rcd->data, len - sizeof(*rcd)); 10350bb77c46STony Luck *id = record_id; 10360bb77c46STony Luck if (uuid_le_cmp(rcd->sec_hdr.section_type, 10370bb77c46STony Luck CPER_SECTION_TYPE_DMESG) == 0) 10380bb77c46STony Luck *type = PSTORE_TYPE_DMESG; 10390bb77c46STony Luck else if (uuid_le_cmp(rcd->sec_hdr.section_type, 10400bb77c46STony Luck CPER_SECTION_TYPE_MCE) == 0) 10410bb77c46STony Luck *type = PSTORE_TYPE_MCE; 10420bb77c46STony Luck else 10430bb77c46STony Luck *type = PSTORE_TYPE_UNKNOWN; 10440bb77c46STony Luck 10450bb77c46STony Luck if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP) 10460bb77c46STony Luck time->tv_sec = rcd->hdr.timestamp; 10470bb77c46STony Luck else 10480bb77c46STony Luck time->tv_sec = 0; 10490bb77c46STony Luck time->tv_nsec = 0; 10500bb77c46STony Luck 105106cf91b4SChen Gong out: 1052f6f82851SKees Cook kfree(rcd); 105306cf91b4SChen Gong return (rc < 0) ? rc : (len - sizeof(*rcd)); 10540bb77c46STony Luck } 10550bb77c46STony Luck 1056b238b8faSChen Gong static int erst_writer(enum pstore_type_id type, u64 *id, unsigned int part, 1057b94fdd07SMatthew Garrett size_t size, struct pstore_info *psi) 10580bb77c46STony Luck { 10590bb77c46STony Luck struct cper_pstore_record *rcd = (struct cper_pstore_record *) 10600bb77c46STony Luck (erst_info.buf - sizeof(*rcd)); 1061b238b8faSChen Gong int ret; 10620bb77c46STony Luck 10630bb77c46STony Luck memset(rcd, 0, sizeof(*rcd)); 10640bb77c46STony Luck memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE); 10650bb77c46STony Luck rcd->hdr.revision = CPER_RECORD_REV; 10660bb77c46STony Luck rcd->hdr.signature_end = CPER_SIG_END; 10670bb77c46STony Luck rcd->hdr.section_count = 1; 10680bb77c46STony Luck rcd->hdr.error_severity = CPER_SEV_FATAL; 10690bb77c46STony Luck /* timestamp valid. platform_id, partition_id are invalid */ 10700bb77c46STony Luck rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP; 10710bb77c46STony Luck rcd->hdr.timestamp = get_seconds(); 10720bb77c46STony Luck rcd->hdr.record_length = sizeof(*rcd) + size; 10730bb77c46STony Luck rcd->hdr.creator_id = CPER_CREATOR_PSTORE; 10740bb77c46STony Luck rcd->hdr.notification_type = CPER_NOTIFY_MCE; 10750bb77c46STony Luck rcd->hdr.record_id = cper_next_record_id(); 10760bb77c46STony Luck rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR; 10770bb77c46STony Luck 10780bb77c46STony Luck rcd->sec_hdr.section_offset = sizeof(*rcd); 10790bb77c46STony Luck rcd->sec_hdr.section_length = size; 10800bb77c46STony Luck rcd->sec_hdr.revision = CPER_SEC_REV; 10810bb77c46STony Luck /* fru_id and fru_text is invalid */ 10820bb77c46STony Luck rcd->sec_hdr.validation_bits = 0; 10830bb77c46STony Luck rcd->sec_hdr.flags = CPER_SEC_PRIMARY; 10840bb77c46STony Luck switch (type) { 10850bb77c46STony Luck case PSTORE_TYPE_DMESG: 10860bb77c46STony Luck rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG; 10870bb77c46STony Luck break; 10880bb77c46STony Luck case PSTORE_TYPE_MCE: 10890bb77c46STony Luck rcd->sec_hdr.section_type = CPER_SECTION_TYPE_MCE; 10900bb77c46STony Luck break; 10910bb77c46STony Luck default: 10920bb77c46STony Luck return -EINVAL; 10930bb77c46STony Luck } 10940bb77c46STony Luck rcd->sec_hdr.section_severity = CPER_SEV_FATAL; 10950bb77c46STony Luck 1096b238b8faSChen Gong ret = erst_write(&rcd->hdr); 1097b238b8faSChen Gong *id = rcd->hdr.record_id; 10980bb77c46STony Luck 1099b238b8faSChen Gong return ret; 11000bb77c46STony Luck } 11010bb77c46STony Luck 110256280682SMatthew Garrett static int erst_clearer(enum pstore_type_id type, u64 id, 110356280682SMatthew Garrett struct pstore_info *psi) 1104638c1fd3SMatthew Garrett { 1105638c1fd3SMatthew Garrett return erst_clear(id); 1106638c1fd3SMatthew Garrett } 1107638c1fd3SMatthew Garrett 1108a08f82d0SHuang Ying static int __init erst_init(void) 1109a08f82d0SHuang Ying { 1110a08f82d0SHuang Ying int rc = 0; 1111a08f82d0SHuang Ying acpi_status status; 1112a08f82d0SHuang Ying struct apei_exec_context ctx; 1113a08f82d0SHuang Ying struct apei_resources erst_resources; 1114a08f82d0SHuang Ying struct resource *r; 11150bb77c46STony Luck char *buf; 1116a08f82d0SHuang Ying 1117a08f82d0SHuang Ying if (acpi_disabled) 1118a08f82d0SHuang Ying goto err; 1119a08f82d0SHuang Ying 1120a08f82d0SHuang Ying if (erst_disable) { 1121a08f82d0SHuang Ying pr_info(ERST_PFX 1122a08f82d0SHuang Ying "Error Record Serialization Table (ERST) support is disabled.\n"); 1123a08f82d0SHuang Ying goto err; 1124a08f82d0SHuang Ying } 1125a08f82d0SHuang Ying 1126a08f82d0SHuang Ying status = acpi_get_table(ACPI_SIG_ERST, 0, 1127a08f82d0SHuang Ying (struct acpi_table_header **)&erst_tab); 1128ad686154SHuang Ying if (status == AE_NOT_FOUND) 1129a08f82d0SHuang Ying goto err; 1130ad686154SHuang Ying else if (ACPI_FAILURE(status)) { 1131a08f82d0SHuang Ying const char *msg = acpi_format_exception(status); 1132a08f82d0SHuang Ying pr_err(ERST_PFX "Failed to get table, %s\n", msg); 1133a08f82d0SHuang Ying rc = -EINVAL; 1134a08f82d0SHuang Ying goto err; 1135a08f82d0SHuang Ying } 1136a08f82d0SHuang Ying 1137a08f82d0SHuang Ying rc = erst_check_table(erst_tab); 1138a08f82d0SHuang Ying if (rc) { 1139a08f82d0SHuang Ying pr_err(FW_BUG ERST_PFX "ERST table is invalid\n"); 1140a08f82d0SHuang Ying goto err; 1141a08f82d0SHuang Ying } 1142a08f82d0SHuang Ying 1143a08f82d0SHuang Ying apei_resources_init(&erst_resources); 1144a08f82d0SHuang Ying erst_exec_ctx_init(&ctx); 1145a08f82d0SHuang Ying rc = apei_exec_collect_resources(&ctx, &erst_resources); 1146a08f82d0SHuang Ying if (rc) 1147a08f82d0SHuang Ying goto err_fini; 1148a08f82d0SHuang Ying rc = apei_resources_request(&erst_resources, "APEI ERST"); 1149a08f82d0SHuang Ying if (rc) 1150a08f82d0SHuang Ying goto err_fini; 1151a08f82d0SHuang Ying rc = apei_exec_pre_map_gars(&ctx); 1152a08f82d0SHuang Ying if (rc) 1153a08f82d0SHuang Ying goto err_release; 1154a08f82d0SHuang Ying rc = erst_get_erange(&erst_erange); 1155a08f82d0SHuang Ying if (rc) { 1156a08f82d0SHuang Ying if (rc == -ENODEV) 1157a08f82d0SHuang Ying pr_info(ERST_PFX 1158a08f82d0SHuang Ying "The corresponding hardware device or firmware implementation " 1159a08f82d0SHuang Ying "is not available.\n"); 1160a08f82d0SHuang Ying else 1161a08f82d0SHuang Ying pr_err(ERST_PFX 1162a08f82d0SHuang Ying "Failed to get Error Log Address Range.\n"); 1163a08f82d0SHuang Ying goto err_unmap_reg; 1164a08f82d0SHuang Ying } 1165a08f82d0SHuang Ying 1166a08f82d0SHuang Ying r = request_mem_region(erst_erange.base, erst_erange.size, "APEI ERST"); 1167a08f82d0SHuang Ying if (!r) { 1168a08f82d0SHuang Ying pr_err(ERST_PFX 1169a08f82d0SHuang Ying "Can not request iomem region <0x%16llx-0x%16llx> for ERST.\n", 1170a08f82d0SHuang Ying (unsigned long long)erst_erange.base, 1171a08f82d0SHuang Ying (unsigned long long)erst_erange.base + erst_erange.size); 1172a08f82d0SHuang Ying rc = -EIO; 1173a08f82d0SHuang Ying goto err_unmap_reg; 1174a08f82d0SHuang Ying } 1175a08f82d0SHuang Ying rc = -ENOMEM; 1176a08f82d0SHuang Ying erst_erange.vaddr = ioremap_cache(erst_erange.base, 1177a08f82d0SHuang Ying erst_erange.size); 1178a08f82d0SHuang Ying if (!erst_erange.vaddr) 1179a08f82d0SHuang Ying goto err_release_erange; 1180a08f82d0SHuang Ying 11810bb77c46STony Luck buf = kmalloc(erst_erange.size, GFP_KERNEL); 1182abd4d558SDon Zickus spin_lock_init(&erst_info.buf_lock); 11830bb77c46STony Luck if (buf) { 11840bb77c46STony Luck erst_info.buf = buf + sizeof(struct cper_pstore_record); 11850bb77c46STony Luck erst_info.bufsize = erst_erange.size - 11860bb77c46STony Luck sizeof(struct cper_pstore_record); 11870bb77c46STony Luck if (pstore_register(&erst_info)) { 11880bb77c46STony Luck pr_info(ERST_PFX "Could not register with persistent store\n"); 11890bb77c46STony Luck kfree(buf); 11900bb77c46STony Luck } 11910bb77c46STony Luck } 11920bb77c46STony Luck 1193a08f82d0SHuang Ying pr_info(ERST_PFX 1194a08f82d0SHuang Ying "Error Record Serialization Table (ERST) support is initialized.\n"); 1195a08f82d0SHuang Ying 1196a08f82d0SHuang Ying return 0; 1197a08f82d0SHuang Ying 1198a08f82d0SHuang Ying err_release_erange: 1199a08f82d0SHuang Ying release_mem_region(erst_erange.base, erst_erange.size); 1200a08f82d0SHuang Ying err_unmap_reg: 1201a08f82d0SHuang Ying apei_exec_post_unmap_gars(&ctx); 1202a08f82d0SHuang Ying err_release: 1203a08f82d0SHuang Ying apei_resources_release(&erst_resources); 1204a08f82d0SHuang Ying err_fini: 1205a08f82d0SHuang Ying apei_resources_fini(&erst_resources); 1206a08f82d0SHuang Ying err: 1207a08f82d0SHuang Ying erst_disable = 1; 1208a08f82d0SHuang Ying return rc; 1209a08f82d0SHuang Ying } 1210a08f82d0SHuang Ying 1211a08f82d0SHuang Ying device_initcall(erst_init); 1212