blob: bb9d29ab2d5e86c9e9a9c559ec0c387f9097917a [file] [log] [blame]
Angel Pons8a3453f2020-04-02 23:48:19 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Kyösti Mälkki034cf632020-01-05 08:12:00 +02002
3#include <console/console.h>
4#include <string.h>
5#include <cbfs.h>
6#include <option.h>
7#include <pc80/mc146818rtc.h>
8#include <types.h>
9
10/* option_table.h is autogenerated */
11#include "option_table.h"
12
13/* Don't warn for checking >= LB_CKS_RANGE_START even though it may be 0. */
14#pragma GCC diagnostic ignored "-Wtype-limits"
15
16/*
17 * This routine returns the value of the requested bits.
Elyes HAOUAS2119d0b2020-02-16 10:01:33 +010018 * input bit = bit count from the beginning of the CMOS image
Kyösti Mälkki034cf632020-01-05 08:12:00 +020019 * length = number of bits to include in the value
20 * ret = a character pointer to where the value is to be returned
21 * returns CB_SUCCESS = successful, cb_err code if an error occurred
22 */
23static enum cb_err get_cmos_value(unsigned long bit, unsigned long length,
24 void *vret)
25{
26 unsigned char *ret;
27 unsigned long byte, byte_bit;
28 unsigned long i;
29 unsigned char uchar;
30
31 /*
32 * The table is checked when it is built to ensure all
33 * values are valid.
34 */
35 ret = vret;
36 byte = bit / 8; /* find the byte where the data starts */
37 byte_bit = bit % 8; /* find the bit in the byte where the data starts */
38 if (length < 9) { /* one byte or less */
39 uchar = cmos_read(byte); /* load the byte */
40 uchar >>= byte_bit; /* shift the bits to byte align */
41 /* clear unspecified bits */
42 ret[0] = uchar & ((1 << length) - 1);
43 } else { /* more than one byte so transfer the whole bytes */
44 for (i = 0; length; i++, length -= 8, byte++) {
45 /* load the byte */
46 ret[i] = cmos_read(byte);
47 }
48 }
49 return CB_SUCCESS;
50}
51
52static enum cb_err locate_cmos_layout(struct region_device *rdev)
53{
54 uint32_t cbfs_type = CBFS_COMPONENT_CMOS_LAYOUT;
Kyösti Mälkkifcbbb912020-04-20 10:21:39 +030055 static struct cbfsf fh;
Kyösti Mälkki034cf632020-01-05 08:12:00 +020056
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 'COREBOOT' region for now.
63 */
64 if (!region_device_sz(&(fh.data))) {
65 if (cbfs_locate_file_in_region(&fh, "COREBOOT", "cmos_layout.bin",
66 &cbfs_type)) {
67 printk(BIOS_ERR, "RTC: cmos_layout.bin could not be found. "
68 "Options are disabled\n");
69 return CB_CMOS_LAYOUT_NOT_FOUND;
70 }
71 }
72
73 cbfs_file_data(rdev, &fh);
74
75 return CB_SUCCESS;
76}
77
78enum cb_err cmos_get_option(void *dest, const char *name)
79{
80 struct cmos_option_table *ct;
81 struct region_device rdev;
82 struct cmos_entries *ce;
83 size_t namelen;
84 int found = 0;
85
86 /* Figure out how long name is */
87 namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);
88
89 if (locate_cmos_layout(&rdev) != CB_SUCCESS) {
90 return CB_CMOS_LAYOUT_NOT_FOUND;
91 }
92 ct = rdev_mmap_full(&rdev);
93 if (!ct) {
94 printk(BIOS_ERR, "RTC: cmos_layout.bin could not be mapped. "
95 "Options are disabled\n");
96
97 return CB_CMOS_LAYOUT_NOT_FOUND;
98 }
99
100 /* find the requested entry record */
101 ce = (struct cmos_entries *)((unsigned char *)ct + ct->header_length);
102 for (; ce->tag == LB_TAG_OPTION;
103 ce = (struct cmos_entries *)((unsigned char *)ce + ce->size)) {
104 if (memcmp(ce->name, name, namelen) == 0) {
105 found = 1;
106 break;
107 }
108 }
109 if (!found) {
110 printk(BIOS_DEBUG, "No CMOS option '%s'.\n", name);
111 rdev_munmap(&rdev, ct);
112 return CB_CMOS_OPTION_NOT_FOUND;
113 }
114
115 if (!cmos_checksum_valid(LB_CKS_RANGE_START, LB_CKS_RANGE_END, LB_CKS_LOC)) {
116 rdev_munmap(&rdev, ct);
117 return CB_CMOS_CHECKSUM_INVALID;
118 }
119 if (get_cmos_value(ce->bit, ce->length, dest) != CB_SUCCESS) {
120 rdev_munmap(&rdev, ct);
121 return CB_CMOS_ACCESS_ERROR;
122 }
123 rdev_munmap(&rdev, ct);
124 return CB_SUCCESS;
125}
126
127static enum cb_err set_cmos_value(unsigned long bit, unsigned long length,
128 void *vret)
129{
130 unsigned char *ret;
131 unsigned long byte, byte_bit;
132 unsigned long i;
133 unsigned char uchar, mask;
134 unsigned int chksum_update_needed = 0;
135
136 ret = vret;
137 byte = bit / 8; /* find the byte where the data starts */
138 byte_bit = bit % 8; /* find the bit where the data starts */
139 if (length <= 8) { /* one byte or less */
140 mask = (1 << length) - 1;
141 mask <<= byte_bit;
142
143 uchar = cmos_read(byte);
144 uchar &= ~mask;
145 uchar |= (ret[0] << byte_bit);
146 cmos_write(uchar, byte);
147 if (byte >= LB_CKS_RANGE_START && byte <= LB_CKS_RANGE_END)
148 chksum_update_needed = 1;
149 } else { /* more that one byte so transfer the whole bytes */
150 if (byte_bit || length % 8)
151 return CB_ERR_ARG;
152
153 for (i = 0; length; i++, length -= 8, byte++) {
154 cmos_write(ret[i], byte);
155 if (byte >= LB_CKS_RANGE_START &&
156 byte <= LB_CKS_RANGE_END)
157 chksum_update_needed = 1;
158 }
159 }
160
161 if (chksum_update_needed) {
162 cmos_set_checksum(LB_CKS_RANGE_START, LB_CKS_RANGE_END,
163 LB_CKS_LOC);
164 }
165 return CB_SUCCESS;
166}
167
168enum cb_err cmos_set_option(const char *name, void *value)
169{
170 struct cmos_option_table *ct;
171 struct region_device rdev;
172 struct cmos_entries *ce;
173 unsigned long length;
174 size_t namelen;
175 int found = 0;
176
177 /* Figure out how long name is */
178 namelen = strnlen(name, CMOS_MAX_NAME_LENGTH);
179
180 if (locate_cmos_layout(&rdev) != CB_SUCCESS) {
181 return CB_CMOS_LAYOUT_NOT_FOUND;
182 }
183 ct = rdev_mmap_full(&rdev);
184 if (!ct) {
185 printk(BIOS_ERR, "RTC: cmos_layout.bin could not be mapped. "
186 "Options are disabled\n");
187
188 return CB_CMOS_LAYOUT_NOT_FOUND;
189 }
190
191 /* find the requested entry record */
192 ce = (struct cmos_entries *)((unsigned char *)ct + ct->header_length);
193 for (; ce->tag == LB_TAG_OPTION;
194 ce = (struct cmos_entries *)((unsigned char *)ce + ce->size)) {
195 if (memcmp(ce->name, name, namelen) == 0) {
196 found = 1;
197 break;
198 }
199 }
200 if (!found) {
201 printk(BIOS_DEBUG, "WARNING: No CMOS option '%s'.\n", name);
202 rdev_munmap(&rdev, ct);
203 return CB_CMOS_OPTION_NOT_FOUND;
204 }
205
206 length = ce->length;
207 if (ce->config == 's') {
208 length = MAX(strlen((const char *)value) * 8, ce->length - 8);
209 /* make sure the string is null terminated */
210 if (set_cmos_value(ce->bit + ce->length - 8, 8, &(u8[]){0})
211 != CB_SUCCESS) {
212 rdev_munmap(&rdev, ct);
213 return CB_CMOS_ACCESS_ERROR;
214 }
215 }
216
217 if (set_cmos_value(ce->bit, length, value) != CB_SUCCESS) {
218 rdev_munmap(&rdev, ct);
219 return CB_CMOS_ACCESS_ERROR;
220 }
221
222 rdev_munmap(&rdev, ct);
223 return CB_SUCCESS;
224}
Kyösti Mälkkib2680a12020-01-04 18:04:39 +0200225
226int cmos_lb_cks_valid(void)
227{
228 return cmos_checksum_valid(LB_CKS_RANGE_START, LB_CKS_RANGE_END, LB_CKS_LOC);
229}
230
Kyösti Mälkkib2680a12020-01-04 18:04:39 +0200231void sanitize_cmos(void)
232{
Bill XIE51ce41c2020-03-29 21:40:04 +0800233 const unsigned char *cmos_default;
234 const bool cmos_need_reset =
235 CONFIG(STATIC_OPTION_TABLE) || cmos_error() || !cmos_lb_cks_valid();
236 size_t length = 128;
237 size_t i;
238
239 if (CONFIG(TPM_MEASURED_BOOT) || cmos_need_reset) {
Julius Werner834b3ec2020-03-04 16:52:08 -0800240 cmos_default = cbfs_map("cmos.default", &length);
Bill XIE51ce41c2020-03-29 21:40:04 +0800241
242 if (!cmos_default || !cmos_need_reset)
243 return;
244
245 u8 control_state = cmos_disable_rtc();
246 for (i = 14; i < MIN(128, length); i++)
247 cmos_write_inner(cmos_default[i], i);
248 cmos_restore_rtc(control_state);
249 }
Kyösti Mälkkib2680a12020-01-04 18:04:39 +0200250}