85 lines
		
	
	
	
		
			2.5 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			85 lines
		
	
	
	
		
			2.5 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * UEFI Common Platform Error Record (CPER) support | ||
|  |  * | ||
|  |  * Copyright (C) 2010, Intel Corp. | ||
|  |  *	Author: Huang Ying <ying.huang@intel.com> | ||
|  |  * | ||
|  |  * CPER is the format used to describe platform hardware error by | ||
|  |  * various APEI tables, such as ERST, BERT and HEST etc. | ||
|  |  * | ||
|  |  * For more information about CPER, please refer to Appendix N of UEFI | ||
|  |  * Specification version 2.3. | ||
|  |  * | ||
|  |  * 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. | ||
|  |  * | ||
|  |  * You should have received a copy of the GNU General Public License | ||
|  |  * along with this program; if not, write to the Free Software | ||
|  |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||
|  |  */ | ||
|  | 
 | ||
|  | #include <linux/kernel.h>
 | ||
|  | #include <linux/module.h>
 | ||
|  | #include <linux/time.h>
 | ||
|  | #include <linux/cper.h>
 | ||
|  | #include <linux/acpi.h>
 | ||
|  | 
 | ||
|  | /*
 | ||
|  |  * CPER record ID need to be unique even after reboot, because record | ||
|  |  * ID is used as index for ERST storage, while CPER records from | ||
|  |  * multiple boot may co-exist in ERST. | ||
|  |  */ | ||
|  | u64 cper_next_record_id(void) | ||
|  | { | ||
|  | 	static atomic64_t seq; | ||
|  | 
 | ||
|  | 	if (!atomic64_read(&seq)) | ||
|  | 		atomic64_set(&seq, ((u64)get_seconds()) << 32); | ||
|  | 
 | ||
|  | 	return atomic64_inc_return(&seq); | ||
|  | } | ||
|  | EXPORT_SYMBOL_GPL(cper_next_record_id); | ||
|  | 
 | ||
|  | int apei_estatus_check_header(const struct acpi_hest_generic_status *estatus) | ||
|  | { | ||
|  | 	if (estatus->data_length && | ||
|  | 	    estatus->data_length < sizeof(struct acpi_hest_generic_data)) | ||
|  | 		return -EINVAL; | ||
|  | 	if (estatus->raw_data_length && | ||
|  | 	    estatus->raw_data_offset < sizeof(*estatus) + estatus->data_length) | ||
|  | 		return -EINVAL; | ||
|  | 
 | ||
|  | 	return 0; | ||
|  | } | ||
|  | EXPORT_SYMBOL_GPL(apei_estatus_check_header); | ||
|  | 
 | ||
|  | int apei_estatus_check(const struct acpi_hest_generic_status *estatus) | ||
|  | { | ||
|  | 	struct acpi_hest_generic_data *gdata; | ||
|  | 	unsigned int data_len, gedata_len; | ||
|  | 	int rc; | ||
|  | 
 | ||
|  | 	rc = apei_estatus_check_header(estatus); | ||
|  | 	if (rc) | ||
|  | 		return rc; | ||
|  | 	data_len = estatus->data_length; | ||
|  | 	gdata = (struct acpi_hest_generic_data *)(estatus + 1); | ||
|  | 	while (data_len > sizeof(*gdata)) { | ||
|  | 		gedata_len = gdata->error_data_length; | ||
|  | 		if (gedata_len > data_len - sizeof(*gdata)) | ||
|  | 			return -EINVAL; | ||
|  | 		data_len -= gedata_len + sizeof(*gdata); | ||
|  | 	} | ||
|  | 	if (data_len) | ||
|  | 		return -EINVAL; | ||
|  | 
 | ||
|  | 	return 0; | ||
|  | } | ||
|  | EXPORT_SYMBOL_GPL(apei_estatus_check); |