soc/intel/ptl: Update ME specification version to 21
[coreboot.git] / src / drivers / pc80 / rtc / option.c
blobcb18e14ae94c0718e1443b29e89244a452e693c1
1 /* SPDX-License-Identifier: GPL-2.0-only */
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>
11 /* option_table.h is autogenerated */
12 #include "option_table.h"
14 /* Don't warn for checking >= LB_CKS_RANGE_START even though it may be 0. */
15 #pragma GCC diagnostic ignored "-Wtype-limits"
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
24 static enum cb_err get_cmos_value(unsigned long bit, unsigned long length,
25 void *vret)
27 unsigned char *ret;
28 unsigned long byte, byte_bit;
29 unsigned long i;
30 unsigned char uchar;
33 * The table is checked when it is built to ensure all
34 * values are valid.
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);
50 return CB_SUCCESS;
53 static struct cmos_option_table *get_cmos_layout(void)
55 static struct cmos_option_table *ct = NULL;
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...
62 * Support only one CMOS layout in the RO CBFS for now.
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;
72 static struct cmos_entries *find_cmos_entry(struct cmos_option_table *ct, const char *name)
74 /* Figure out how long name is */
75 const size_t namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);
76 struct cmos_entries *ce;
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;
85 return NULL;
88 static enum cb_err cmos_get_uint_option(unsigned int *dest, const char *name)
90 struct cmos_option_table *ct;
91 struct cmos_entries *ce;
93 ct = get_cmos_layout();
94 if (!ct)
95 return CB_CMOS_LAYOUT_NOT_FOUND;
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;
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;
108 if (!cmos_checksum_valid(LB_CKS_RANGE_START, LB_CKS_RANGE_END, LB_CKS_LOC))
109 return CB_CMOS_CHECKSUM_INVALID;
111 if (get_cmos_value(ce->bit, ce->length, dest) != CB_SUCCESS)
112 return CB_CMOS_ACCESS_ERROR;
114 return CB_SUCCESS;
117 unsigned int get_uint_option(const char *name, const unsigned int fallback)
119 unsigned int value = 0;
120 return cmos_get_uint_option(&value, name) == CB_SUCCESS ? value : fallback;
123 static enum cb_err set_cmos_value(unsigned long bit, unsigned long length,
124 void *vret)
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;
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;
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;
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;
157 if (chksum_update_needed) {
158 cmos_set_checksum(LB_CKS_RANGE_START, LB_CKS_RANGE_END,
159 LB_CKS_LOC);
161 return CB_SUCCESS;
164 static enum cb_err cmos_set_uint_option(const char *name, unsigned int *value)
166 struct cmos_option_table *ct;
167 struct cmos_entries *ce;
169 ct = get_cmos_layout();
170 if (!ct)
171 return CB_CMOS_LAYOUT_NOT_FOUND;
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;
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;
184 if (set_cmos_value(ce->bit, ce->length, value) != CB_SUCCESS)
185 return CB_CMOS_ACCESS_ERROR;
187 return CB_SUCCESS;
190 enum cb_err set_uint_option(const char *name, unsigned int value)
192 return cmos_set_uint_option(name, &value);
195 int cmos_lb_cks_valid(void)
197 return cmos_checksum_valid(LB_CKS_RANGE_START, LB_CKS_RANGE_END, LB_CKS_LOC);
200 void sanitize_cmos(void)
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;
209 if (CONFIG(TPM_MEASURED_BOOT) || cmos_need_reset) {
210 cmos_default = cbfs_map("cmos.default", &length);
212 if (!cmos_default || !cmos_need_reset)
213 return;
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);