cregit-Linux how code gets into the kernel

Release 4.11 drivers/acpi/apei/erst.c

/*
 * APEI Error Record Serialization Table support
 *
 * ERST is a way provided by APEI to save and retrieve hardware error
 * information to and from a persistent store.
 *
 * For more information about ERST, please refer to ACPI Specification
 * version 4.0, section 17.4.
 *
 * Copyright 2010 Intel Corp.
 *   Author: Huang Ying <ying.huang@intel.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/acpi.h>
#include <linux/uaccess.h>
#include <linux/cper.h>
#include <linux/nmi.h>
#include <linux/hardirq.h>
#include <linux/pstore.h>
#include <linux/vmalloc.h>
#include <linux/mm.h> /* kvfree() */
#include <acpi/apei.h>

#include "apei-internal.h"


#undef pr_fmt

#define pr_fmt(fmt) "ERST: " fmt

/* ERST command status */

#define ERST_STATUS_SUCCESS			0x0

#define ERST_STATUS_NOT_ENOUGH_SPACE		0x1

#define ERST_STATUS_HARDWARE_NOT_AVAILABLE	0x2

#define ERST_STATUS_FAILED			0x3

#define ERST_STATUS_RECORD_STORE_EMPTY		0x4

#define ERST_STATUS_RECORD_NOT_FOUND		0x5


#define ERST_TAB_ENTRY(tab)						\
	((struct acpi_whea_header *)((char *)(tab) +                    \
                                     sizeof(struct acpi_table_erst)))


#define SPIN_UNIT		100			
/* 100ns */
/* Firmware should respond within 1 milliseconds */

#define FIRMWARE_TIMEOUT	(1 * NSEC_PER_MSEC)

#define FIRMWARE_MAX_STALL	50			
/* 50us */


int erst_disable;

EXPORT_SYMBOL_GPL(erst_disable);


static struct acpi_table_erst *erst_tab;

/* ERST Error Log Address Range atrributes */

#define ERST_RANGE_RESERVED	0x0001

#define ERST_RANGE_NVRAM	0x0002

#define ERST_RANGE_SLOW		0x0004

/*
 * ERST Error Log Address Range, used as buffer for reading/writing
 * error records.
 */

static struct erst_erange {
	
u64 base;
	
u64 size;
	
void __iomem *vaddr;
	
u32 attr;
} 
erst_erange;

/*
 * Prevent ERST interpreter to run simultaneously, because the
 * corresponding firmware implementation may not work properly when
 * invoked simultaneously.
 *
 * It is used to provide exclusive accessing for ERST Error Log
 * Address Range too.
 */
static DEFINE_RAW_SPINLOCK(erst_lock);


static inline int erst_errno(int command_status) { switch (command_status) { case ERST_STATUS_SUCCESS: return 0; case ERST_STATUS_HARDWARE_NOT_AVAILABLE: return -ENODEV; case ERST_STATUS_NOT_ENOUGH_SPACE: return -ENOSPC; case ERST_STATUS_RECORD_STORE_EMPTY: case ERST_STATUS_RECORD_NOT_FOUND: return -ENOENT; default: return -EINVAL; } }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying51100.00%1100.00%
Total51100.00%1100.00%


static int erst_timedout(u64 *t, u64 spin_unit) { if ((s64)*t < spin_unit) { pr_warn(FW_WARN "Firmware does not respond in time.\n"); return 1; } *t -= spin_unit; ndelay(spin_unit); touch_nmi_watchdog(); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying4692.00%150.00%
Borislav Petkov48.00%150.00%
Total50100.00%2100.00%


static int erst_exec_load_var1(struct apei_exec_context *ctx, struct acpi_whea_header *entry) { return __apei_exec_read_register(entry, &ctx->var1); }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying27100.00%1100.00%
Total27100.00%1100.00%


static int erst_exec_load_var2(struct apei_exec_context *ctx, struct acpi_whea_header *entry) { return __apei_exec_read_register(entry, &ctx->var2); }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying27100.00%1100.00%
Total27100.00%1100.00%


static int erst_exec_store_var1(struct apei_exec_context *ctx, struct acpi_whea_header *entry) { return __apei_exec_write_register(entry, ctx->var1); }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying26100.00%1100.00%
Total26100.00%1100.00%


static int erst_exec_add(struct apei_exec_context *ctx, struct acpi_whea_header *entry) { ctx->var1 += ctx->var2; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying27100.00%1100.00%
Total27100.00%1100.00%


static int erst_exec_subtract(struct apei_exec_context *ctx, struct acpi_whea_header *entry) { ctx->var1 -= ctx->var2; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying27100.00%1100.00%
Total27100.00%1100.00%


static int erst_exec_add_value(struct apei_exec_context *ctx, struct acpi_whea_header *entry) { int rc; u64 val; rc = __apei_exec_read_register(entry, &val); if (rc) return rc; val += ctx->value; rc = __apei_exec_write_register(entry, val); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying57100.00%1100.00%
Total57100.00%1100.00%


static int erst_exec_subtract_value(struct apei_exec_context *ctx, struct acpi_whea_header *entry) { int rc; u64 val; rc = __apei_exec_read_register(entry, &val); if (rc) return rc; val -= ctx->value; rc = __apei_exec_write_register(entry, val); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying57100.00%1100.00%
Total57100.00%1100.00%


static int erst_exec_stall(struct apei_exec_context *ctx, struct acpi_whea_header *entry) { u64 stall_time; if (ctx->value > FIRMWARE_MAX_STALL) { if (!in_nmi()) pr_warn(FW_WARN "Too long stall time for stall instruction: 0x%llx.\n", ctx->value); stall_time = FIRMWARE_MAX_STALL; } else stall_time = ctx->value; udelay(stall_time); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying5789.06%150.00%
Borislav Petkov710.94%150.00%
Total64100.00%2100.00%


static int erst_exec_stall_while_true(struct apei_exec_context *ctx, struct acpi_whea_header *entry) { int rc; u64 val; u64 timeout = FIRMWARE_TIMEOUT; u64 stall_time; if (ctx->var1 > FIRMWARE_MAX_STALL) { if (!in_nmi()) pr_warn(FW_WARN "Too long stall time for stall while true instruction: 0x%llx.\n", ctx->var1); stall_time = FIRMWARE_MAX_STALL; } else stall_time = ctx->var1; for (;;) { rc = __apei_exec_read_register(entry, &val); if (rc) return rc; if (val != ctx->value) break; if (erst_timedout(&timeout, stall_time * NSEC_PER_USEC)) return -EIO; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying11294.12%150.00%
Borislav Petkov75.88%150.00%
Total119100.00%2100.00%


static int erst_exec_skip_next_instruction_if_true( struct apei_exec_context *ctx, struct acpi_whea_header *entry) { int rc; u64 val; rc = __apei_exec_read_register(entry, &val); if (rc) return rc; if (val == ctx->value) { ctx->ip += 2; return APEI_EXEC_SET_IP; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying61100.00%1100.00%
Total61100.00%1100.00%


static int erst_exec_goto(struct apei_exec_context *ctx, struct acpi_whea_header *entry) { ctx->ip = ctx->value; return APEI_EXEC_SET_IP; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying27100.00%1100.00%
Total27100.00%1100.00%


static int erst_exec_set_src_address_base(struct apei_exec_context *ctx, struct acpi_whea_header *entry) { return __apei_exec_read_register(entry, &ctx->src_base); }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying27100.00%1100.00%
Total27100.00%1100.00%


static int erst_exec_set_dst_address_base(struct apei_exec_context *ctx, struct acpi_whea_header *entry) { return __apei_exec_read_register(entry, &ctx->dst_base); }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying27100.00%1100.00%
Total27100.00%1100.00%


static int erst_exec_move_data(struct apei_exec_context *ctx, struct acpi_whea_header *entry) { int rc; u64 offset; void *src, *dst; /* ioremap does not work in interrupt context */ if (in_interrupt()) { pr_warn("MOVE_DATA can not be used in interrupt context.\n"); return -EBUSY; } rc = __apei_exec_read_register(entry, &offset); if (rc) return rc; src = ioremap(ctx->src_base + offset, ctx->var2); if (!src) return -ENOMEM; dst = ioremap(ctx->dst_base + offset, ctx->var2); if (!dst) { iounmap(src); return -ENOMEM; } memmove(dst, src, ctx->var2); iounmap(src); iounmap(dst); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying13393.66%250.00%
Wei Yongjun74.93%125.00%
Borislav Petkov21.41%125.00%
Total142100.00%4100.00%

static struct apei_exec_ins_type erst_ins_type[] = { [ACPI_ERST_READ_REGISTER] = { .flags = APEI_EXEC_INS_ACCESS_REGISTER, .run = apei_exec_read_register, }, [ACPI_ERST_READ_REGISTER_VALUE] = { .flags = APEI_EXEC_INS_ACCESS_REGISTER, .run = apei_exec_read_register_value, }, [ACPI_ERST_WRITE_REGISTER] = { .flags = APEI_EXEC_INS_ACCESS_REGISTER, .run = apei_exec_write_register, }, [ACPI_ERST_WRITE_REGISTER_VALUE] = { .flags = APEI_EXEC_INS_ACCESS_REGISTER, .run = apei_exec_write_register_value, }, [ACPI_ERST_NOOP] = { .flags = 0, .run = apei_exec_noop, }, [ACPI_ERST_LOAD_VAR1] = { .flags = APEI_EXEC_INS_ACCESS_REGISTER, .run = erst_exec_load_var1, }, [ACPI_ERST_LOAD_VAR2] = { .flags = APEI_EXEC_INS_ACCESS_REGISTER, .run = erst_exec_load_var2, }, [ACPI_ERST_STORE_VAR1] = { .flags = APEI_EXEC_INS_ACCESS_REGISTER, .run = erst_exec_store_var1, }, [ACPI_ERST_ADD] = { .flags = 0, .run = erst_exec_add, }, [ACPI_ERST_SUBTRACT] = { .flags = 0, .run = erst_exec_subtract, }, [ACPI_ERST_ADD_VALUE] = { .flags = APEI_EXEC_INS_ACCESS_REGISTER, .run = erst_exec_add_value, }, [ACPI_ERST_SUBTRACT_VALUE] = { .flags = APEI_EXEC_INS_ACCESS_REGISTER, .run = erst_exec_subtract_value, }, [ACPI_ERST_STALL] = { .flags = 0, .run = erst_exec_stall, }, [ACPI_ERST_STALL_WHILE_TRUE] = { .flags = APEI_EXEC_INS_ACCESS_REGISTER, .run = erst_exec_stall_while_true, }, [ACPI_ERST_SKIP_NEXT_IF_TRUE] = { .flags = APEI_EXEC_INS_ACCESS_REGISTER, .run = erst_exec_skip_next_instruction_if_true, }, [ACPI_ERST_GOTO] = { .flags = 0, .run = erst_exec_goto, }, [ACPI_ERST_SET_SRC_ADDRESS_BASE] = { .flags = APEI_EXEC_INS_ACCESS_REGISTER, .run = erst_exec_set_src_address_base, }, [ACPI_ERST_SET_DST_ADDRESS_BASE] = { .flags = APEI_EXEC_INS_ACCESS_REGISTER, .run = erst_exec_set_dst_address_base, }, [ACPI_ERST_MOVE_DATA] = { .flags = APEI_EXEC_INS_ACCESS_REGISTER, .run = erst_exec_move_data, }, };
static inline void erst_exec_ctx_init(struct apei_exec_context *ctx) { apei_exec_ctx_init(ctx, erst_ins_type, ARRAY_SIZE(erst_ins_type), ERST_TAB_ENTRY(erst_tab), erst_tab->entries); }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying33100.00%1100.00%
Total33100.00%1100.00%


static int erst_get_erange(struct erst_erange *range) { struct apei_exec_context ctx; int rc; erst_exec_ctx_init(&ctx); rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_RANGE); if (rc) return rc; range->base = apei_exec_ctx_get_output(&ctx); rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_LENGTH); if (rc) return rc; range->size = apei_exec_ctx_get_output(&ctx); rc = apei_exec_run(&ctx, ACPI_ERST_GET_ERROR_ATTRIBUTES); if (rc) return rc; range->attr = apei_exec_ctx_get_output(&ctx); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying108100.00%1100.00%
Total108100.00%1100.00%


static ssize_t __erst_get_record_count(void) { struct apei_exec_context ctx; int rc; erst_exec_ctx_init(&ctx); rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_COUNT); if (rc) return rc; return apei_exec_ctx_get_output(&ctx); }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying45100.00%1100.00%
Total45100.00%1100.00%


ssize_t erst_get_record_count(void) { ssize_t count; unsigned long flags; if (erst_disable) return -ENODEV; raw_spin_lock_irqsave(&erst_lock, flags); count = __erst_get_record_count(); raw_spin_unlock_irqrestore(&erst_lock, flags); return count; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying46100.00%2100.00%
Total46100.00%2100.00%

EXPORT_SYMBOL_GPL(erst_get_record_count); #define ERST_RECORD_ID_CACHE_SIZE_MIN 16 #define ERST_RECORD_ID_CACHE_SIZE_MAX 1024 struct erst_record_id_cache { struct mutex lock; u64 *entries; int len; int size; int refcount; }; static struct erst_record_id_cache erst_record_id_cache = { .lock = __MUTEX_INITIALIZER(erst_record_id_cache.lock), .refcount = 0, };
static int __erst_get_next_record_id(u64 *record_id) { struct apei_exec_context ctx; int rc; erst_exec_ctx_init(&ctx); rc = apei_exec_run(&ctx, ACPI_ERST_GET_RECORD_ID); if (rc) return rc; *record_id = apei_exec_ctx_get_output(&ctx); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying52100.00%2100.00%
Total52100.00%2100.00%


int erst_get_record_id_begin(int *pos) { int rc; if (erst_disable) return -ENODEV; rc = mutex_lock_interruptible(&erst_record_id_cache.lock); if (rc) return rc; erst_record_id_cache.refcount++; mutex_unlock(&erst_record_id_cache.lock); *pos = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying58100.00%1100.00%
Total58100.00%1100.00%

EXPORT_SYMBOL_GPL(erst_get_record_id_begin); /* erst_record_id_cache.lock must be held by caller */
static int __erst_record_id_cache_add_one(void) { u64 id, prev_id, first_id; int i, rc; u64 *entries; unsigned long flags; id = prev_id = first_id = APEI_ERST_INVALID_RECORD_ID; retry: raw_spin_lock_irqsave(&erst_lock, flags); rc = __erst_get_next_record_id(&id); raw_spin_unlock_irqrestore(&erst_lock, flags); if (rc == -ENOENT) return 0; if (rc) return rc; if (id == APEI_ERST_INVALID_RECORD_ID) return 0; /* can not skip current ID, or loop back to first ID */ if (id == prev_id || id == first_id) return 0; if (first_id == APEI_ERST_INVALID_RECORD_ID) first_id = id; prev_id = id; entries = erst_record_id_cache.entries; for (i = 0; i < erst_record_id_cache.len; i++) { if (entries[i] == id) break; } /* record id already in cache, try next */ if (i < erst_record_id_cache.len) goto retry; if (erst_record_id_cache.len >= erst_record_id_cache.size) { int new_size, alloc_size; u64 *new_entries; new_size = erst_record_id_cache.size * 2; new_size = clamp_val(new_size, ERST_RECORD_ID_CACHE_SIZE_MIN, ERST_RECORD_ID_CACHE_SIZE_MAX); if (new_size <= erst_record_id_cache.size) { if (printk_ratelimit()) pr_warn(FW_WARN "too many record IDs!\n"); return 0; } alloc_size = new_size * sizeof(entries[0]); if (alloc_size < PAGE_SIZE) new_entries = kmalloc(alloc_size, GFP_KERNEL); else new_entries = vmalloc(alloc_size); if (!new_entries) return -ENOMEM; memcpy(new_entries, entries, erst_record_id_cache.len * sizeof(entries[0])); kvfree(entries); erst_record_id_cache.entries = entries = new_entries; erst_record_id_cache.size = new_size; } entries[i] = id; erst_record_id_cache.len++; return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying31798.45%133.33%
Borislav Petkov41.24%133.33%
Tetsuo Handa10.31%133.33%
Total322100.00%3100.00%

/* * Get the record ID of an existing error record on the persistent * storage. If there is no error record on the persistent storage, the * returned record_id is APEI_ERST_INVALID_RECORD_ID. */
int erst_get_record_id_next(int *pos, u64 *record_id) { int rc = 0; u64 *entries; if (erst_disable) return -ENODEV; /* must be enclosed by erst_get_record_id_begin/end */ BUG_ON(!erst_record_id_cache.refcount); BUG_ON(*pos < 0 || *pos > erst_record_id_cache.len); mutex_lock(&erst_record_id_cache.lock); entries = erst_record_id_cache.entries; for (; *pos < erst_record_id_cache.len; (*pos)++) if (entries[*pos] != APEI_ERST_INVALID_RECORD_ID) break; /* found next record id in cache */ if (*pos < erst_record_id_cache.len) { *record_id = entries[*pos]; (*pos)++; goto out_unlock; } /* Try to add one more record ID to cache */ rc = __erst_record_id_cache_add_one(); if (rc < 0) goto out_unlock; /* successfully add one new ID */ if (rc == 1) { *record_id = erst_record_id_cache.entries[*pos]; (*pos)++; rc = 0; } else { *pos = -1; *record_id = APEI_ERST_INVALID_RECORD_ID; } out_unlock: mutex_unlock(&erst_record_id_cache.lock); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying197100.00%1100.00%
Total197100.00%1100.00%

EXPORT_SYMBOL_GPL(erst_get_record_id_next); /* erst_record_id_cache.lock must be held by caller */
static void __erst_record_id_cache_compact(void) { int i, wpos = 0; u64 *entries; if (erst_record_id_cache.refcount) return; entries = erst_record_id_cache.entries; for (i = 0; i < erst_record_id_cache.len; i++) { if (entries[i] == APEI_ERST_INVALID_RECORD_ID) continue; if (wpos != i) entries[wpos] = entries[i]; wpos++; } erst_record_id_cache.len = wpos; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying8398.81%266.67%
Chen Gong11.19%133.33%
Total84100.00%3100.00%


void erst_get_record_id_end(void) { /* * erst_disable != 0 should be detected by invoker via the * return value of erst_get_record_id_begin/next, so this * function should not be called for erst_disable != 0. */ BUG_ON(erst_disable); mutex_lock(&erst_record_id_cache.lock); erst_record_id_cache.refcount--; BUG_ON(erst_record_id_cache.refcount < 0); __erst_record_id_cache_compact(); mutex_unlock(&erst_record_id_cache.lock); }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying46100.00%2100.00%
Total46100.00%2100.00%

EXPORT_SYMBOL_GPL(erst_get_record_id_end);
static int __erst_write_to_storage(u64 offset) { struct apei_exec_context ctx; u64 timeout = FIRMWARE_TIMEOUT; u64 val; int rc; erst_exec_ctx_init(&ctx); rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_WRITE); if (rc) return rc; apei_exec_ctx_set_input(&ctx, offset); rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET); if (rc) return rc; rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION); if (rc) return rc; for (;;) { rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS); if (rc) return rc; val = apei_exec_ctx_get_output(&ctx); if (!val) break; if (erst_timedout(&timeout, SPIN_UNIT)) return -EIO; } rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS); if (rc) return rc; val = apei_exec_ctx_get_output(&ctx); rc = apei_exec_run_optional(&ctx, ACPI_ERST_END); if (rc) return rc; return erst_errno(val); }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying189100.00%2100.00%
Total189100.00%2100.00%


static int __erst_read_from_storage(u64 record_id, u64 offset) { struct apei_exec_context ctx; u64 timeout = FIRMWARE_TIMEOUT; u64 val; int rc; erst_exec_ctx_init(&ctx); rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_READ); if (rc) return rc; apei_exec_ctx_set_input(&ctx, offset); rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_OFFSET); if (rc) return rc; apei_exec_ctx_set_input(&ctx, record_id); rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID); if (rc) return rc; rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION); if (rc) return rc; for (;;) { rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS); if (rc) return rc; val = apei_exec_ctx_get_output(&ctx); if (!val) break; if (erst_timedout(&timeout, SPIN_UNIT)) return -EIO; }; rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS); if (rc) return rc; val = apei_exec_ctx_get_output(&ctx); rc = apei_exec_run_optional(&ctx, ACPI_ERST_END); if (rc) return rc; return erst_errno(val); }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying218100.00%2100.00%
Total218100.00%2100.00%


static int __erst_clear_from_storage(u64 record_id) { struct apei_exec_context ctx; u64 timeout = FIRMWARE_TIMEOUT; u64 val; int rc; erst_exec_ctx_init(&ctx); rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_CLEAR); if (rc) return rc; apei_exec_ctx_set_input(&ctx, record_id); rc = apei_exec_run(&ctx, ACPI_ERST_SET_RECORD_ID); if (rc) return rc; rc = apei_exec_run(&ctx, ACPI_ERST_EXECUTE_OPERATION); if (rc) return rc; for (;;) { rc = apei_exec_run(&ctx, ACPI_ERST_CHECK_BUSY_STATUS); if (rc) return rc; val = apei_exec_ctx_get_output(&ctx); if (!val) break; if (erst_timedout(&timeout, SPIN_UNIT)) return -EIO; } rc = apei_exec_run(&ctx, ACPI_ERST_GET_COMMAND_STATUS); if (rc) return rc; val = apei_exec_ctx_get_output(&ctx); rc = apei_exec_run_optional(&ctx, ACPI_ERST_END); if (rc) return rc; return erst_errno(val); }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying189100.00%2100.00%
Total189100.00%2100.00%

/* NVRAM ERST Error Log Address Range is not supported yet */
static void pr_unimpl_nvram(void) { if (printk_ratelimit()) pr_warn("NVRAM ERST Log Address Range not implemented yet.\n"); }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying1688.89%150.00%
Borislav Petkov211.11%150.00%
Total18100.00%2100.00%


static int __erst_write_to_nvram(const struct cper_record_header *record) { /* do not print message, because printk is not safe for NMI */ return -ENOSYS; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying17100.00%1100.00%
Total17100.00%1100.00%


static int __erst_read_to_erange_from_nvram(u64 record_id, u64 *offset) { pr_unimpl_nvram(); return -ENOSYS; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying20100.00%1100.00%
Total20100.00%1100.00%


static int __erst_clear_from_nvram(u64 record_id) { pr_unimpl_nvram(); return -ENOSYS; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying16100.00%1100.00%
Total16100.00%1100.00%


int erst_write(const struct cper_record_header *record) { int rc; unsigned long flags; struct cper_record_header *rcd_erange; if (erst_disable) return -ENODEV; if (memcmp(record->signature, CPER_SIG_RECORD, CPER_SIG_SIZE)) return -EINVAL; if (erst_erange.attr & ERST_RANGE_NVRAM) { if (!raw_spin_trylock_irqsave(&erst_lock, flags)) return -EBUSY; rc = __erst_write_to_nvram(record); raw_spin_unlock_irqrestore(&erst_lock, flags); return rc; } if (record->record_length > erst_erange.size) return -EINVAL; if (!raw_spin_trylock_irqsave(&erst_lock, flags)) return -EBUSY; memcpy(erst_erange.vaddr, record, record->record_length); rcd_erange = erst_erange.vaddr; /* signature for serialization system */ memcpy(&rcd_erange->persistence_information, "ER", 2); rc = __erst_write_to_storage(0); raw_spin_unlock_irqrestore(&erst_lock, flags); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying170100.00%2100.00%
Total170100.00%2100.00%

EXPORT_SYMBOL_GPL(erst_write);
static int __erst_read_to_erange(u64 record_id, u64 *offset) { int rc; if (erst_erange.attr & ERST_RANGE_NVRAM) return __erst_read_to_erange_from_nvram( record_id, offset); rc = __erst_read_from_storage(record_id, 0); if (rc) return rc; *offset = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying56100.00%1100.00%
Total56100.00%1100.00%


static ssize_t __erst_read(u64 record_id, struct cper_record_header *record, size_t buflen) { int rc; u64 offset, len = 0; struct cper_record_header *rcd_tmp; rc = __erst_read_to_erange(record_id, &offset); if (rc) return rc; rcd_tmp = erst_erange.vaddr + offset; len = rcd_tmp->record_length; if (len <= buflen) memcpy(record, rcd_tmp, len); return len; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying81100.00%1100.00%
Total81100.00%1100.00%

/* * If return value > buflen, the buffer size is not big enough, * else if return value < 0, something goes wrong, * else everything is OK, and return value is record length */
ssize_t erst_read(u64 record_id, struct cper_record_header *record, size_t buflen) { ssize_t len; unsigned long flags; if (erst_disable) return -ENODEV; raw_spin_lock_irqsave(&erst_lock, flags); len = __erst_read(record_id, record, buflen); raw_spin_unlock_irqrestore(&erst_lock, flags); return len; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying61100.00%2100.00%
Total61100.00%2100.00%

EXPORT_SYMBOL_GPL(erst_read);
int erst_clear(u64 record_id) { int rc, i; unsigned long flags; u64 *entries; if (erst_disable) return -ENODEV; rc = mutex_lock_interruptible(&erst_record_id_cache.lock); if (rc) return rc; raw_spin_lock_irqsave(&erst_lock, flags); if (erst_erange.attr & ERST_RANGE_NVRAM) rc = __erst_clear_from_nvram(record_id); else rc = __erst_clear_from_storage(record_id); raw_spin_unlock_irqrestore(&erst_lock, flags); if (rc) goto out; entries = erst_record_id_cache.entries; for (i = 0; i < erst_record_id_cache.len; i++) { if (entries[i] == record_id) entries[i] = APEI_ERST_INVALID_RECORD_ID; } __erst_record_id_cache_compact(); out: mutex_unlock(&erst_record_id_cache.lock); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying147100.00%3100.00%
Total147100.00%3100.00%

EXPORT_SYMBOL_GPL(erst_clear);
static int __init setup_erst_disable(char *str) { erst_disable = 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying18100.00%1100.00%
Total18100.00%1100.00%

__setup("erst_disable", setup_erst_disable);
static int erst_check_table(struct acpi_table_erst *erst_tab) { if ((erst_tab->header_length != (sizeof(struct acpi_table_erst) - sizeof(erst_tab->header))) && (erst_tab->header_length != sizeof(struct acpi_table_erst))) return -EINVAL; if (erst_tab->header.length < sizeof(struct acpi_table_erst)) return -EINVAL; if (erst_tab->entries != (erst_tab->header.length - sizeof(struct acpi_table_erst)) / sizeof(struct acpi_erst_entry)) return -EINVAL; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying10099.01%266.67%
Jiang Liu10.99%133.33%
Total101100.00%3100.00%

static int erst_open_pstore(struct pstore_info *psi); static int erst_close_pstore(struct pstore_info *psi); static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, struct timespec *time, char **buf, bool *compressed, ssize_t *ecc_notice_size, struct pstore_info *psi); static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, unsigned int part, int count, bool compressed, size_t size, struct pstore_info *psi); static int erst_clearer(enum pstore_type_id type, u64 id, int count, struct timespec time, struct pstore_info *psi); static struct pstore_info erst_info = { .owner = THIS_MODULE, .name = "erst", .flags = PSTORE_FLAGS_DMESG, .open = erst_open_pstore, .close = erst_close_pstore, .read = erst_reader, .write = erst_writer, .erase = erst_clearer }; #define CPER_CREATOR_PSTORE \ UUID_LE(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c, \ 0x64, 0x90, 0xb8, 0x9d) #define CPER_SECTION_TYPE_DMESG \ UUID_LE(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54, \ 0x94, 0x19, 0xeb, 0x12) #define CPER_SECTION_TYPE_DMESG_Z \ UUID_LE(0x4f118707, 0x04dd, 0x4055, 0xb5, 0xdd, 0x95, 0x6d, \ 0x34, 0xdd, 0xfa, 0xc6) #define CPER_SECTION_TYPE_MCE \ UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96, \ 0x04, 0x4a, 0x38, 0xfc) struct cper_pstore_record { struct cper_record_header hdr; struct cper_section_descriptor sec_hdr; char data[]; } __packed; static int reader_pos;
static int erst_open_pstore(struct pstore_info *psi) { int rc; if (erst_disable) return -ENODEV; rc = erst_get_record_id_begin(&reader_pos); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Chen Gong33100.00%1100.00%
Total33100.00%1100.00%


static int erst_close_pstore(struct pstore_info *psi) { erst_get_record_id_end(); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Chen Gong17100.00%1100.00%
Total17100.00%1100.00%


static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, struct timespec *time, char **buf, bool *compressed, ssize_t *ecc_notice_size, struct pstore_info *psi) { int rc; ssize_t len = 0; u64 record_id; struct cper_pstore_record *rcd; size_t rcd_len = sizeof(*rcd) + erst_info.bufsize; if (erst_disable) return -ENODEV; rcd = kmalloc(rcd_len, GFP_KERNEL); if (!rcd) { rc = -ENOMEM; goto out; } skip: rc = erst_get_record_id_next(&reader_pos, &record_id); if (rc) goto out; /* no more record */ if (record_id == APEI_ERST_INVALID_RECORD_ID) { rc = -EINVAL; goto out; } len = erst_read(record_id, &rcd->hdr, rcd_len); /* The record may be cleared by others, try read next record */ if (len == -ENOENT) goto skip; else if (len < sizeof(*rcd)) { rc = -EIO; goto out; } if (uuid_le_cmp(rcd->hdr.creator_id, CPER_CREATOR_PSTORE) != 0) goto skip; *buf = kmalloc(len, GFP_KERNEL); if (*buf == NULL) { rc = -ENOMEM; goto out; } memcpy(*buf, rcd->data, len - sizeof(*rcd)); *id = record_id; *compressed = false; *ecc_notice_size = 0; if (uuid_le_cmp(rcd->sec_hdr.section_type, CPER_SECTION_TYPE_DMESG_Z) == 0) { *type = PSTORE_TYPE_DMESG; *compressed = true; } else if (uuid_le_cmp(rcd->sec_hdr.section_type, CPER_SECTION_TYPE_DMESG) == 0) *type = PSTORE_TYPE_DMESG; else if (uuid_le_cmp(rcd->sec_hdr.section_type, CPER_SECTION_TYPE_MCE) == 0) *type = PSTORE_TYPE_MCE; else *type = PSTORE_TYPE_UNKNOWN; if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP) time->tv_sec = rcd->hdr.timestamp; else time->tv_sec = 0; time->tv_nsec = 0; out: kfree(rcd); return (rc < 0) ? rc : (len - sizeof(*rcd)); }

Contributors

PersonTokensPropCommitsCommitProp
Tony Luck15438.69%19.09%
Kees Cook9423.62%19.09%
Chen Gong5513.82%327.27%
Huang Ying4010.05%19.09%
Aruna Balakrishnaiah379.30%218.18%
Geliang Tang92.26%19.09%
Matthew Garrett51.26%19.09%
Seiji Aguchi41.01%19.09%
Total398100.00%11100.00%


static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, u64 *id, unsigned int part, int count, bool compressed, size_t size, struct pstore_info *psi) { struct cper_pstore_record *rcd = (struct cper_pstore_record *) (erst_info.buf - sizeof(*rcd)); int ret; memset(rcd, 0, sizeof(*rcd)); memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE); rcd->hdr.revision = CPER_RECORD_REV; rcd->hdr.signature_end = CPER_SIG_END; rcd->hdr.section_count = 1; rcd->hdr.error_severity = CPER_SEV_FATAL; /* timestamp valid. platform_id, partition_id are invalid */ rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP; rcd->hdr.timestamp = get_seconds(); rcd->hdr.record_length = sizeof(*rcd) + size; rcd->hdr.creator_id = CPER_CREATOR_PSTORE; rcd->hdr.notification_type = CPER_NOTIFY_MCE; rcd->hdr.record_id = cper_next_record_id(); rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR; rcd->sec_hdr.section_offset = sizeof(*rcd); rcd->sec_hdr.section_length = size; rcd->sec_hdr.revision = CPER_SEC_REV; /* fru_id and fru_text is invalid */ rcd->sec_hdr.validation_bits = 0; rcd->sec_hdr.flags = CPER_SEC_PRIMARY; switch (type) { case PSTORE_TYPE_DMESG: if (compressed) rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG_Z; else rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG; break; case PSTORE_TYPE_MCE: rcd->sec_hdr.section_type = CPER_SECTION_TYPE_MCE; break; default: return -EINVAL; } rcd->sec_hdr.section_severity = CPER_SEV_FATAL; ret = erst_write(&rcd->hdr); *id = rcd->hdr.record_id; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Tony Luck25884.04%110.00%
Aruna Balakrishnaiah175.54%330.00%
Chen Gong165.21%110.00%
Matthew Garrett92.93%330.00%
Kees Cook41.30%110.00%
Seiji Aguchi30.98%110.00%
Total307100.00%10100.00%


static int erst_clearer(enum pstore_type_id type, u64 id, int count, struct timespec time, struct pstore_info *psi) { return erst_clear(id); }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Garrett2477.42%250.00%
Seiji Aguchi722.58%250.00%
Total31100.00%4100.00%


static int __init erst_init(void) { int rc = 0; acpi_status status; struct apei_exec_context ctx; struct apei_resources erst_resources; struct resource *r; char *buf; if (acpi_disabled) goto err; if (erst_disable) { pr_info( "Error Record Serialization Table (ERST) support is disabled.\n"); goto err; } status = acpi_get_table(ACPI_SIG_ERST, 0, (struct acpi_table_header **)&erst_tab); if (status == AE_NOT_FOUND) goto err; else if (ACPI_FAILURE(status)) { const char *msg = acpi_format_exception(status); pr_err("Failed to get table, %s\n", msg); rc = -EINVAL; goto err; } rc = erst_check_table(erst_tab); if (rc) { pr_err(FW_BUG "ERST table is invalid.\n"); goto err; } apei_resources_init(&erst_resources); erst_exec_ctx_init(&ctx); rc = apei_exec_collect_resources(&ctx, &erst_resources); if (rc) goto err_fini; rc = apei_resources_request(&erst_resources, "APEI ERST"); if (rc) goto err_fini; rc = apei_exec_pre_map_gars(&ctx); if (rc) goto err_release; rc = erst_get_erange(&erst_erange); if (rc) { if (rc == -ENODEV) pr_info( "The corresponding hardware device or firmware implementation " "is not available.\n"); else pr_err("Failed to get Error Log Address Range.\n"); goto err_unmap_reg; } r = request_mem_region(erst_erange.base, erst_erange.size, "APEI ERST"); if (!r) { pr_err("Can not request [mem %#010llx-%#010llx] for ERST.\n", (unsigned long long)erst_erange.base, (unsigned long long)erst_erange.base + erst_erange.size - 1); rc = -EIO; goto err_unmap_reg; } rc = -ENOMEM; erst_erange.vaddr = ioremap_cache(erst_erange.base, erst_erange.size); if (!erst_erange.vaddr) goto err_release_erange; pr_info( "Error Record Serialization Table (ERST) support is initialized.\n"); buf = kmalloc(erst_erange.size, GFP_KERNEL); spin_lock_init(&erst_info.buf_lock); if (buf) { erst_info.buf = buf + sizeof(struct cper_pstore_record); erst_info.bufsize = erst_erange.size - sizeof(struct cper_pstore_record); rc = pstore_register(&erst_info); if (rc) { if (rc != -EPERM) pr_info( "Could not register with persistent store.\n"); erst_info.buf = NULL; erst_info.bufsize = 0; kfree(buf); } } else pr_err( "Failed to allocate %lld bytes for persistent store error log.\n", erst_erange.size); /* Cleanup ERST Resources */ apei_resources_fini(&erst_resources); return 0; err_release_erange: release_mem_region(erst_erange.base, erst_erange.size); err_unmap_reg: apei_exec_post_unmap_gars(&ctx); err_release: apei_resources_release(&erst_resources); err_fini: apei_resources_fini(&erst_resources); err: erst_disable = 1; return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Huang Ying36874.95%116.67%
Tony Luck7014.26%116.67%
Lenny Szubowicz367.33%116.67%
Borislav Petkov81.63%116.67%
Joshua Hunt71.43%116.67%
Don Zickus20.41%116.67%
Total491100.00%6100.00%

device_initcall(erst_init);

Overall Contributors

PersonTokensPropCommitsCommitProp
Huang Ying403478.01%616.22%
Tony Luck58111.24%25.41%
Chen Gong1623.13%513.51%
Kees Cook1072.07%25.41%
Matthew Garrett701.35%38.11%
Aruna Balakrishnaiah671.30%410.81%
Borislav Petkov420.81%12.70%
Lenny Szubowicz360.70%12.70%
Seiji Aguchi280.54%25.41%
Geliang Tang130.25%12.70%
Joshua Hunt70.14%12.70%
Wei Yongjun70.14%12.70%
Tetsuo Handa50.10%12.70%
Thomas Gleixner30.06%12.70%
Stephen Rothwell30.06%12.70%
Don Zickus20.04%12.70%
Jiang Liu10.02%12.70%
Namhyung Kim10.02%12.70%
Jarkko Nikula10.02%12.70%
Stefan Weil10.02%12.70%
Total5171100.00%37100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.