• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <acpi/acpi.h>
4 #include <console/console.h>
5 #include <string.h>
6 #include <cbfs.h>
7 #include <option.h>
8 #include <pc80/mc146818rtc.h>
9 #include <types.h>
10 
11 /* option_table.h is autogenerated */
12 #include "option_table.h"
13 
14 /* Don't warn for checking >= LB_CKS_RANGE_START even though it may be 0. */
15 #pragma GCC diagnostic ignored "-Wtype-limits"
16 
17 /*
18  * This routine returns the value of the requested bits.
19  * input bit = bit count from the beginning of the CMOS image
20  * length = number of bits to include in the value
21  * ret = a character pointer to where the value is to be returned
22  * returns CB_SUCCESS = successful, cb_err code if an error occurred
23  */
get_cmos_value(unsigned long bit,unsigned long length,void * vret)24 static enum cb_err get_cmos_value(unsigned long bit, unsigned long length,
25 				  void *vret)
26 {
27 	unsigned char *ret;
28 	unsigned long byte, byte_bit;
29 	unsigned long i;
30 	unsigned char uchar;
31 
32 	/*
33 	 * The table is checked when it is built to ensure all
34 	 * values are valid.
35 	 */
36 	ret = vret;
37 	byte = bit / 8;	/* find the byte where the data starts */
38 	byte_bit = bit % 8; /* find the bit in the byte where the data starts */
39 	if (length < 9) {	/* one byte or less */
40 		uchar = cmos_read(byte); /* load the byte */
41 		uchar >>= byte_bit;	/* shift the bits to byte align */
42 		/* clear unspecified bits */
43 		ret[0] = uchar & ((1 << length) - 1);
44 	} else {	/* more than one byte so transfer the whole bytes */
45 		for (i = 0; length; i++, length -= 8, byte++) {
46 			/* load the byte */
47 			ret[i] = cmos_read(byte);
48 		}
49 	}
50 	return CB_SUCCESS;
51 }
52 
get_cmos_layout(void)53 static struct cmos_option_table *get_cmos_layout(void)
54 {
55 	static struct cmos_option_table *ct = NULL;
56 
57 	/*
58 	 * In case VBOOT is enabled and this function is called from SMM,
59 	 * we have multiple CMOS layout files and to locate them we'd need to
60 	 * include VBOOT into SMM...
61 	 *
62 	 * Support only one CMOS layout in the RO CBFS for now.
63 	 */
64 	if (!ct)
65 		ct = cbfs_ro_map("cmos_layout.bin", NULL);
66 	if (!ct)
67 		printk(BIOS_ERR, "RTC: cmos_layout.bin could not be found. "
68 				 "Options are disabled\n");
69 	return ct;
70 }
71 
find_cmos_entry(struct cmos_option_table * ct,const char * name)72 static struct cmos_entries *find_cmos_entry(struct cmos_option_table *ct, const char *name)
73 {
74 	/* Figure out how long name is */
75 	const size_t namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);
76 	struct cmos_entries *ce;
77 
78 	/* Find the requested entry record */
79 	ce = (struct cmos_entries *)((unsigned char *)ct + ct->header_length);
80 	for (; ce->tag == LB_TAG_OPTION;
81 		ce = (struct cmos_entries *)((unsigned char *)ce + ce->size)) {
82 		if (memcmp(ce->name, name, namelen) == 0)
83 			return ce;
84 	}
85 	return NULL;
86 }
87 
cmos_get_uint_option(unsigned int * dest,const char * name)88 static enum cb_err cmos_get_uint_option(unsigned int *dest, const char *name)
89 {
90 	struct cmos_option_table *ct;
91 	struct cmos_entries *ce;
92 
93 	ct = get_cmos_layout();
94 	if (!ct)
95 		return CB_CMOS_LAYOUT_NOT_FOUND;
96 
97 	ce = find_cmos_entry(ct, name);
98 	if (!ce) {
99 		printk(BIOS_DEBUG, "No CMOS option '%s'.\n", name);
100 		return CB_CMOS_OPTION_NOT_FOUND;
101 	}
102 
103 	if (ce->config != 'e' && ce->config != 'h') {
104 		printk(BIOS_ERR, "CMOS option '%s' is not of integer type.\n", name);
105 		return CB_ERR_ARG;
106 	}
107 
108 	if (!cmos_checksum_valid(LB_CKS_RANGE_START, LB_CKS_RANGE_END, LB_CKS_LOC))
109 		return CB_CMOS_CHECKSUM_INVALID;
110 
111 	if (get_cmos_value(ce->bit, ce->length, dest) != CB_SUCCESS)
112 		return CB_CMOS_ACCESS_ERROR;
113 
114 	return CB_SUCCESS;
115 }
116 
get_uint_option(const char * name,const unsigned int fallback)117 unsigned int get_uint_option(const char *name, const unsigned int fallback)
118 {
119 	unsigned int value = 0;
120 	return cmos_get_uint_option(&value, name) == CB_SUCCESS ? value : fallback;
121 }
122 
set_cmos_value(unsigned long bit,unsigned long length,void * vret)123 static enum cb_err set_cmos_value(unsigned long bit, unsigned long length,
124 				  void *vret)
125 {
126 	unsigned char *ret;
127 	unsigned long byte, byte_bit;
128 	unsigned long i;
129 	unsigned char uchar, mask;
130 	unsigned int chksum_update_needed = 0;
131 
132 	ret = vret;
133 	byte = bit / 8;		/* find the byte where the data starts */
134 	byte_bit = bit % 8;	/* find the bit where the data starts */
135 	if (length <= 8) {	/* one byte or less */
136 		mask = (1 << length) - 1;
137 		mask <<= byte_bit;
138 
139 		uchar = cmos_read(byte);
140 		uchar &= ~mask;
141 		uchar |= (ret[0] << byte_bit);
142 		cmos_write(uchar, byte);
143 		if (byte >= LB_CKS_RANGE_START && byte <= LB_CKS_RANGE_END)
144 			chksum_update_needed = 1;
145 	} else { /* more that one byte so transfer the whole bytes */
146 		if (byte_bit || length % 8)
147 			return CB_ERR_ARG;
148 
149 		for (i = 0; length; i++, length -= 8, byte++) {
150 			cmos_write(ret[i], byte);
151 			if (byte >= LB_CKS_RANGE_START &&
152 			    byte <= LB_CKS_RANGE_END)
153 				chksum_update_needed = 1;
154 		}
155 	}
156 
157 	if (chksum_update_needed) {
158 		cmos_set_checksum(LB_CKS_RANGE_START, LB_CKS_RANGE_END,
159 				  LB_CKS_LOC);
160 	}
161 	return CB_SUCCESS;
162 }
163 
cmos_set_uint_option(const char * name,unsigned int * value)164 static enum cb_err cmos_set_uint_option(const char *name, unsigned int *value)
165 {
166 	struct cmos_option_table *ct;
167 	struct cmos_entries *ce;
168 
169 	ct = get_cmos_layout();
170 	if (!ct)
171 		return CB_CMOS_LAYOUT_NOT_FOUND;
172 
173 	ce = find_cmos_entry(ct, name);
174 	if (!ce) {
175 		printk(BIOS_DEBUG, "WARNING: No CMOS option '%s'.\n", name);
176 		return CB_CMOS_OPTION_NOT_FOUND;
177 	}
178 
179 	if (ce->config != 'e' && ce->config != 'h') {
180 		printk(BIOS_ERR, "CMOS option '%s' is not of integer type.\n", name);
181 		return CB_ERR_ARG;
182 	}
183 
184 	if (set_cmos_value(ce->bit, ce->length, value) != CB_SUCCESS)
185 		return CB_CMOS_ACCESS_ERROR;
186 
187 	return CB_SUCCESS;
188 }
189 
set_uint_option(const char * name,unsigned int value)190 enum cb_err set_uint_option(const char *name, unsigned int value)
191 {
192 	return cmos_set_uint_option(name, &value);
193 }
194 
cmos_lb_cks_valid(void)195 int cmos_lb_cks_valid(void)
196 {
197 	return cmos_checksum_valid(LB_CKS_RANGE_START, LB_CKS_RANGE_END, LB_CKS_LOC);
198 }
199 
sanitize_cmos(void)200 void sanitize_cmos(void)
201 {
202 	const unsigned char *cmos_default;
203 	const bool cmos_need_reset =
204 		(CONFIG(STATIC_OPTION_TABLE) || cmos_error() || !cmos_lb_cks_valid())
205 		&& !acpi_is_wakeup_s3();
206 	size_t length = 128;
207 	size_t i;
208 
209 	if (CONFIG(TPM_MEASURED_BOOT) || cmos_need_reset) {
210 		cmos_default = cbfs_map("cmos.default", &length);
211 
212 		if (!cmos_default || !cmos_need_reset)
213 			return;
214 
215 		u8 control_state = cmos_disable_rtc();
216 		/* Copy checked range and the checksum from the default */
217 		for (i = LB_CKS_RANGE_START; i < MIN(LB_CKS_RANGE_END + 1, length); i++)
218 			cmos_write_inner(cmos_default[i], i);
219 		/* CMOS checksum takes 2 bytes */
220 		cmos_write_inner(cmos_default[LB_CKS_LOC], LB_CKS_LOC);
221 		cmos_write_inner(cmos_default[LB_CKS_LOC + 1], LB_CKS_LOC + 1);
222 		cmos_restore_rtc(control_state);
223 	}
224 }
225