Change-Id: I2858fdf74e782f425d56653491cdebe83c185d19 Signed-off-by: Patrick Georgi <pgeorgi@google.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/41208 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: HAOUAS Elyes <ehaouas@noos.fr>
		
			
				
	
	
		
			213 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			213 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* SPDX-License-Identifier: GPL-2.0-only */
 | |
| 
 | |
| #include "common.h"
 | |
| #include "cmos_ops.h"
 | |
| #include "cmos_lowlevel.h"
 | |
| 
 | |
| static int prepare_cmos_op_common(const cmos_entry_t * e);
 | |
| 
 | |
| /****************************************************************************
 | |
|  * prepare_cmos_op_common
 | |
|  *
 | |
|  * Perform a few checks common to both reads and writes.
 | |
|  ****************************************************************************/
 | |
| static int prepare_cmos_op_common(const cmos_entry_t * e)
 | |
| {
 | |
| 	int result;
 | |
| 
 | |
| 	if (e->config == CMOS_ENTRY_RESERVED)
 | |
| 		/* Access to reserved parameters is not permitted. */
 | |
| 		return CMOS_OP_RESERVED;
 | |
| 
 | |
| 	if ((result = verify_cmos_op(e->bit, e->length, e->config)) != OK)
 | |
| 		return result;
 | |
| 
 | |
| 	assert(e->length > 0);
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|  * prepare_cmos_read
 | |
|  *
 | |
|  * The caller wishes to read a CMOS parameter represented by 'e'.  Perform
 | |
|  * sanity checking on 'e'.  If a problem was found with e, return an error
 | |
|  * code.  Else return OK.
 | |
|  ****************************************************************************/
 | |
| int prepare_cmos_read(const cmos_entry_t * e)
 | |
| {
 | |
| 	int result;
 | |
| 
 | |
| 	if ((result = prepare_cmos_op_common(e)) != OK)
 | |
| 		return result;
 | |
| 
 | |
| 	switch (e->config) {
 | |
| 	case CMOS_ENTRY_ENUM:
 | |
| 	case CMOS_ENTRY_HEX:
 | |
| 	case CMOS_ENTRY_STRING:
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		BUG();
 | |
| 	}
 | |
| 
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|  * prepare_cmos_write
 | |
|  *
 | |
|  * The caller wishes to set a CMOS parameter represented by 'e' to a value
 | |
|  * whose string representation is stored in 'value_str'.  Perform sanity
 | |
|  * checking on 'value_str'.  On error, return an error code.  Else store the
 | |
|  * numeric equivalent of 'value_str' in '*value' and return OK.
 | |
|  ****************************************************************************/
 | |
| int prepare_cmos_write(const cmos_entry_t * e, const char value_str[],
 | |
| 		       unsigned long long *value)
 | |
| {
 | |
| 	const cmos_enum_t *q;
 | |
| 	unsigned long long out;
 | |
| 	const char *p;
 | |
| 	char *memory = NULL;
 | |
| 	int negative, result, found_one;
 | |
| 
 | |
| 	if ((result = prepare_cmos_op_common(e)) != OK)
 | |
| 		return result;
 | |
| 
 | |
| 	switch (e->config) {
 | |
| 	case CMOS_ENTRY_ENUM:
 | |
| 		/* Make sure the user's input corresponds to a valid option. */
 | |
| 		for (q = first_cmos_enum_id(e->config_id), found_one = 0;
 | |
| 		     q != NULL; q = next_cmos_enum_id(q)) {
 | |
| 			found_one = 1;
 | |
| 
 | |
| 			if (!strncmp(q->text, value_str, CMOS_MAX_TEXT_LENGTH))
 | |
| 				break;
 | |
| 		}
 | |
| 
 | |
| 		if (!found_one)
 | |
| 			return CMOS_OP_NO_MATCHING_ENUM;
 | |
| 
 | |
| 		if (q == NULL)
 | |
| 			return CMOS_OP_BAD_ENUM_VALUE;
 | |
| 
 | |
| 		out = q->value;
 | |
| 		break;
 | |
| 
 | |
| 	case CMOS_ENTRY_HEX:
 | |
| 		/* See if the first character of 'value_str' (excluding
 | |
| 		 * any initial whitespace) is a minus sign.
 | |
| 		 */
 | |
| 		for (p = value_str; isspace((int)(unsigned char)*p); p++) ;
 | |
| 		negative = (*p == '-');
 | |
| 
 | |
| 		out = strtoull(value_str, (char **)&p, 0);
 | |
| 
 | |
| 		if (*p)
 | |
| 			return CMOS_OP_INVALID_INT;
 | |
| 
 | |
| 		/* If we get this far, the user specified a valid integer.
 | |
| 		 * However we do not currently support the use of negative
 | |
| 		 * numbers as CMOS parameter values.
 | |
| 		 */
 | |
| 		if (negative)
 | |
| 			return CMOS_OP_NEGATIVE_INT;
 | |
| 
 | |
| 		break;
 | |
| 
 | |
| 	case CMOS_ENTRY_STRING:
 | |
| 		if (e->length < (8 * strlen(value_str)))
 | |
| 			return CMOS_OP_VALUE_TOO_WIDE;
 | |
| 		memory = malloc(e->length / 8);
 | |
| 		memset(memory, 0, e->length / 8);
 | |
| 		strcpy(memory, value_str);
 | |
| 		out = (unsigned long)memory;
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		BUG();
 | |
| 	}
 | |
| 
 | |
| 	if ((e->length < (8 * sizeof(*value))) && (out >= (1ull << e->length))) {
 | |
| 		if (memory) free(memory);
 | |
| 		return CMOS_OP_VALUE_TOO_WIDE;
 | |
| 	}
 | |
| 
 | |
| 	*value = out;
 | |
| 	return OK;
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|  * cmos_checksum_read
 | |
|  *
 | |
|  * Read the checksum for the coreboot parameters stored in CMOS and return
 | |
|  * this value.
 | |
|  ****************************************************************************/
 | |
| uint16_t cmos_checksum_read(void)
 | |
| {
 | |
| 	uint16_t lo, hi;
 | |
| 
 | |
| 	/* The checksum is stored in a big-endian format. */
 | |
| 	hi = cmos_read_byte(cmos_checksum_index);
 | |
| 	lo = cmos_read_byte(cmos_checksum_index + 1);
 | |
| 	return (hi << 8) + lo;
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|  * cmos_checksum_write
 | |
|  *
 | |
|  * Set the checksum for the coreboot parameters stored in CMOS to
 | |
|  * 'checksum'.
 | |
|  ****************************************************************************/
 | |
| void cmos_checksum_write(uint16_t checksum)
 | |
| {
 | |
| 	unsigned char lo, hi;
 | |
| 
 | |
| 	/* The checksum is stored in a big-endian format. */
 | |
| 	hi = (unsigned char)(checksum >> 8);
 | |
| 	lo = (unsigned char)(checksum & 0x00ff);
 | |
| 	cmos_write_byte(cmos_checksum_index, hi);
 | |
| 	cmos_write_byte(cmos_checksum_index + 1, lo);
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|  * cmos_checksum_compute
 | |
|  *
 | |
|  * Compute a checksum for the coreboot parameter values currently stored in
 | |
|  * CMOS and return this checksum.
 | |
|  ****************************************************************************/
 | |
| uint16_t cmos_checksum_compute(void)
 | |
| {
 | |
| 	unsigned i, sum;
 | |
| 
 | |
| 	sum = 0;
 | |
| 
 | |
| 	for (i = cmos_checksum_start; i <= cmos_checksum_end; i++)
 | |
| 		sum += cmos_read_byte(i);
 | |
| 
 | |
| 	return (uint16_t)(sum & 0xffff);
 | |
| }
 | |
| 
 | |
| /****************************************************************************
 | |
|  * cmos_checksum_verify
 | |
|  *
 | |
|  * Verify that the coreboot CMOS checksum is valid.  If checksum is not
 | |
|  * valid then print warning message and exit.
 | |
|  ****************************************************************************/
 | |
| void cmos_checksum_verify(void)
 | |
| {
 | |
| 	uint16_t computed, actual;
 | |
| 
 | |
| 	set_iopl(3);
 | |
| 	computed = cmos_checksum_compute();
 | |
| 	actual = cmos_checksum_read();
 | |
| 	set_iopl(0);
 | |
| 
 | |
| 	if (computed != actual) {
 | |
| 		fprintf(stderr, "%s: Warning: coreboot CMOS checksum is bad.\n",
 | |
| 			prog_name);
 | |
| 		fprintf(stderr, "Computed checksum: 0x%x. Stored checksum: 0x%x\n",
 | |
| 			computed, actual);
 | |
| 		exit(1);
 | |
| 	}
 | |
| }
 |