/ util / nvramtool / cmos_ops.c
cmos_ops.c
  1  /* SPDX-License-Identifier: GPL-2.0-only */
  2  
  3  #include "common.h"
  4  #include "cmos_ops.h"
  5  #include "cmos_lowlevel.h"
  6  
  7  static int prepare_cmos_op_common(const cmos_entry_t * e);
  8  
  9  /****************************************************************************
 10   * prepare_cmos_op_common
 11   *
 12   * Perform a few checks common to both reads and writes.
 13   ****************************************************************************/
 14  static int prepare_cmos_op_common(const cmos_entry_t * e)
 15  {
 16  	int result;
 17  
 18  	if (e->config == CMOS_ENTRY_RESERVED)
 19  		/* Access to reserved parameters is not permitted. */
 20  		return CMOS_OP_RESERVED;
 21  
 22  	if ((result = verify_cmos_op(e->bit, e->length, e->config)) != OK)
 23  		return result;
 24  
 25  	assert(e->length > 0);
 26  	return OK;
 27  }
 28  
 29  /****************************************************************************
 30   * prepare_cmos_read
 31   *
 32   * The caller wishes to read a CMOS parameter represented by 'e'.  Perform
 33   * sanity checking on 'e'.  If a problem was found with e, return an error
 34   * code.  Else return OK.
 35   ****************************************************************************/
 36  int prepare_cmos_read(const cmos_entry_t * e)
 37  {
 38  	int result;
 39  
 40  	if ((result = prepare_cmos_op_common(e)) != OK)
 41  		return result;
 42  
 43  	switch (e->config) {
 44  	case CMOS_ENTRY_ENUM:
 45  	case CMOS_ENTRY_HEX:
 46  	case CMOS_ENTRY_STRING:
 47  		break;
 48  
 49  	default:
 50  		BUG();
 51  	}
 52  
 53  	return OK;
 54  }
 55  
 56  /****************************************************************************
 57   * prepare_cmos_write
 58   *
 59   * The caller wishes to set a CMOS parameter represented by 'e' to a value
 60   * whose string representation is stored in 'value_str'.  Perform sanity
 61   * checking on 'value_str'.  On error, return an error code.  Else store the
 62   * numeric equivalent of 'value_str' in '*value' and return OK.
 63   ****************************************************************************/
 64  int prepare_cmos_write(const cmos_entry_t * e, const char value_str[],
 65  		       unsigned long long *value)
 66  {
 67  	const cmos_enum_t *q;
 68  	unsigned long long out;
 69  	const char *p;
 70  	char *memory = NULL;
 71  	int negative, result, found_one;
 72  
 73  	if ((result = prepare_cmos_op_common(e)) != OK)
 74  		return result;
 75  
 76  	switch (e->config) {
 77  	case CMOS_ENTRY_ENUM:
 78  		/* Make sure the user's input corresponds to a valid option. */
 79  		for (q = first_cmos_enum_id(e->config_id), found_one = 0;
 80  		     q != NULL; q = next_cmos_enum_id(q)) {
 81  			found_one = 1;
 82  
 83  			if (!strncmp(q->text, value_str, CMOS_MAX_TEXT_LENGTH))
 84  				break;
 85  		}
 86  
 87  		if (!found_one)
 88  			return CMOS_OP_NO_MATCHING_ENUM;
 89  
 90  		if (q == NULL)
 91  			return CMOS_OP_BAD_ENUM_VALUE;
 92  
 93  		out = q->value;
 94  		break;
 95  
 96  	case CMOS_ENTRY_HEX:
 97  		/* See if the first character of 'value_str' (excluding
 98  		 * any initial whitespace) is a minus sign.
 99  		 */
100  		for (p = value_str; isspace((int)(unsigned char)*p); p++) ;
101  		negative = (*p == '-');
102  
103  		out = strtoull(value_str, (char **)&p, 0);
104  
105  		if (*p)
106  			return CMOS_OP_INVALID_INT;
107  
108  		/* If we get this far, the user specified a valid integer.
109  		 * However we do not currently support the use of negative
110  		 * numbers as CMOS parameter values.
111  		 */
112  		if (negative)
113  			return CMOS_OP_NEGATIVE_INT;
114  
115  		break;
116  
117  	case CMOS_ENTRY_STRING:
118  		if (e->length < (8 * strlen(value_str)))
119  			return CMOS_OP_VALUE_TOO_WIDE;
120  		memory = malloc(e->length / 8);
121  		memset(memory, 0, e->length / 8);
122  		strcpy(memory, value_str);
123  		out = (unsigned long)memory;
124  		break;
125  
126  	default:
127  		BUG();
128  	}
129  
130  	if ((e->length < (8 * sizeof(*value))) && (out >= (1ull << e->length))) {
131  		if (memory) free(memory);
132  		return CMOS_OP_VALUE_TOO_WIDE;
133  	}
134  
135  	*value = out;
136  	return OK;
137  }
138  
139  /****************************************************************************
140   * cmos_checksum_read
141   *
142   * Read the checksum for the coreboot parameters stored in CMOS and return
143   * this value.
144   ****************************************************************************/
145  uint16_t cmos_checksum_read(void)
146  {
147  	uint16_t lo, hi;
148  
149  	/* The checksum is stored in a big-endian format. */
150  	hi = cmos_read_byte(cmos_checksum_index);
151  	lo = cmos_read_byte(cmos_checksum_index + 1);
152  	return (hi << 8) + lo;
153  }
154  
155  /****************************************************************************
156   * cmos_checksum_write
157   *
158   * Set the checksum for the coreboot parameters stored in CMOS to
159   * 'checksum'.
160   ****************************************************************************/
161  void cmos_checksum_write(uint16_t checksum)
162  {
163  	unsigned char lo, hi;
164  
165  	/* The checksum is stored in a big-endian format. */
166  	hi = (unsigned char)(checksum >> 8);
167  	lo = (unsigned char)(checksum & 0x00ff);
168  	cmos_write_byte(cmos_checksum_index, hi);
169  	cmos_write_byte(cmos_checksum_index + 1, lo);
170  }
171  
172  /****************************************************************************
173   * cmos_checksum_compute
174   *
175   * Compute a checksum for the coreboot parameter values currently stored in
176   * CMOS and return this checksum.
177   ****************************************************************************/
178  uint16_t cmos_checksum_compute(void)
179  {
180  	unsigned i, sum;
181  
182  	sum = 0;
183  
184  	for (i = cmos_checksum_start; i <= cmos_checksum_end; i++)
185  		sum += cmos_read_byte(i);
186  
187  	return (uint16_t)(sum & 0xffff);
188  }
189  
190  /****************************************************************************
191   * cmos_checksum_verify
192   *
193   * Verify that the coreboot CMOS checksum is valid.  If checksum is not
194   * valid then print warning message and exit.
195   ****************************************************************************/
196  void cmos_checksum_verify(void)
197  {
198  	uint16_t computed, actual;
199  
200  	set_iopl(3);
201  	computed = cmos_checksum_compute();
202  	actual = cmos_checksum_read();
203  	set_iopl(0);
204  
205  	if (computed != actual) {
206  		fprintf(stderr, "%s: Warning: coreboot CMOS checksum is bad.\n",
207  			prog_name);
208  		fprintf(stderr, "Computed checksum: 0x%x. Stored checksum: 0x%x\n",
209  			computed, actual);
210  		exit(1);
211  	}
212  }