Angel Pons | 8a3453f | 2020-04-02 23:48:19 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 2 | |
| 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 HAOUAS | 2119d0b | 2020-02-16 10:01:33 +0100 | [diff] [blame] | 18 | * input bit = bit count from the beginning of the CMOS image |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 19 | * 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 | */ |
| 23 | static 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 | |
Julius Werner | 9d0cc2a | 2020-01-22 18:00:18 -0800 | [diff] [blame] | 52 | static struct cmos_option_table *get_cmos_layout(void) |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 53 | { |
Julius Werner | 9d0cc2a | 2020-01-22 18:00:18 -0800 | [diff] [blame] | 54 | static struct cmos_option_table *ct = NULL; |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 55 | |
| 56 | /* |
| 57 | * In case VBOOT is enabled and this function is called from SMM, |
| 58 | * we have multiple CMOS layout files and to locate them we'd need to |
| 59 | * include VBOOT into SMM... |
| 60 | * |
Julius Werner | 9d0cc2a | 2020-01-22 18:00:18 -0800 | [diff] [blame] | 61 | * Support only one CMOS layout in the RO CBFS for now. |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 62 | */ |
Julius Werner | 9d0cc2a | 2020-01-22 18:00:18 -0800 | [diff] [blame] | 63 | if (!ct) |
| 64 | ct = cbfs_ro_map("cmos_layout.bin", NULL); |
| 65 | if (!ct) |
| 66 | printk(BIOS_ERR, "RTC: cmos_layout.bin could not be found. " |
| 67 | "Options are disabled\n"); |
| 68 | return ct; |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 69 | } |
| 70 | |
Angel Pons | 109a9ec | 2021-04-23 12:58:42 +0200 | [diff] [blame^] | 71 | static struct cmos_entries *find_cmos_entry(struct cmos_option_table *ct, const char *name) |
| 72 | { |
| 73 | /* Figure out how long name is */ |
| 74 | const size_t namelen = strnlen(name, CMOS_MAX_NAME_LENGTH); |
| 75 | struct cmos_entries *ce; |
| 76 | |
| 77 | /* Find the requested entry record */ |
| 78 | ce = (struct cmos_entries *)((unsigned char *)ct + ct->header_length); |
| 79 | for (; ce->tag == LB_TAG_OPTION; |
| 80 | ce = (struct cmos_entries *)((unsigned char *)ce + ce->size)) { |
| 81 | if (memcmp(ce->name, name, namelen) == 0) |
| 82 | return ce; |
| 83 | } |
| 84 | return NULL; |
| 85 | } |
| 86 | |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 87 | enum cb_err cmos_get_option(void *dest, const char *name) |
| 88 | { |
| 89 | struct cmos_option_table *ct; |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 90 | struct cmos_entries *ce; |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 91 | |
Julius Werner | 9d0cc2a | 2020-01-22 18:00:18 -0800 | [diff] [blame] | 92 | ct = get_cmos_layout(); |
| 93 | if (!ct) |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 94 | return CB_CMOS_LAYOUT_NOT_FOUND; |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 95 | |
Angel Pons | 109a9ec | 2021-04-23 12:58:42 +0200 | [diff] [blame^] | 96 | ce = find_cmos_entry(ct, name); |
| 97 | if (!ce) { |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 98 | printk(BIOS_DEBUG, "No CMOS option '%s'.\n", name); |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 99 | return CB_CMOS_OPTION_NOT_FOUND; |
| 100 | } |
| 101 | |
Julius Werner | 9d0cc2a | 2020-01-22 18:00:18 -0800 | [diff] [blame] | 102 | if (!cmos_checksum_valid(LB_CKS_RANGE_START, LB_CKS_RANGE_END, LB_CKS_LOC)) |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 103 | return CB_CMOS_CHECKSUM_INVALID; |
Julius Werner | 9d0cc2a | 2020-01-22 18:00:18 -0800 | [diff] [blame] | 104 | |
| 105 | if (get_cmos_value(ce->bit, ce->length, dest) != CB_SUCCESS) |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 106 | return CB_CMOS_ACCESS_ERROR; |
Julius Werner | 9d0cc2a | 2020-01-22 18:00:18 -0800 | [diff] [blame] | 107 | |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 108 | return CB_SUCCESS; |
| 109 | } |
| 110 | |
| 111 | static enum cb_err set_cmos_value(unsigned long bit, unsigned long length, |
| 112 | void *vret) |
| 113 | { |
| 114 | unsigned char *ret; |
| 115 | unsigned long byte, byte_bit; |
| 116 | unsigned long i; |
| 117 | unsigned char uchar, mask; |
| 118 | unsigned int chksum_update_needed = 0; |
| 119 | |
| 120 | ret = vret; |
| 121 | byte = bit / 8; /* find the byte where the data starts */ |
| 122 | byte_bit = bit % 8; /* find the bit where the data starts */ |
| 123 | if (length <= 8) { /* one byte or less */ |
| 124 | mask = (1 << length) - 1; |
| 125 | mask <<= byte_bit; |
| 126 | |
| 127 | uchar = cmos_read(byte); |
| 128 | uchar &= ~mask; |
| 129 | uchar |= (ret[0] << byte_bit); |
| 130 | cmos_write(uchar, byte); |
| 131 | if (byte >= LB_CKS_RANGE_START && byte <= LB_CKS_RANGE_END) |
| 132 | chksum_update_needed = 1; |
| 133 | } else { /* more that one byte so transfer the whole bytes */ |
| 134 | if (byte_bit || length % 8) |
| 135 | return CB_ERR_ARG; |
| 136 | |
| 137 | for (i = 0; length; i++, length -= 8, byte++) { |
| 138 | cmos_write(ret[i], byte); |
| 139 | if (byte >= LB_CKS_RANGE_START && |
| 140 | byte <= LB_CKS_RANGE_END) |
| 141 | chksum_update_needed = 1; |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | if (chksum_update_needed) { |
| 146 | cmos_set_checksum(LB_CKS_RANGE_START, LB_CKS_RANGE_END, |
| 147 | LB_CKS_LOC); |
| 148 | } |
| 149 | return CB_SUCCESS; |
| 150 | } |
| 151 | |
| 152 | enum cb_err cmos_set_option(const char *name, void *value) |
| 153 | { |
| 154 | struct cmos_option_table *ct; |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 155 | struct cmos_entries *ce; |
| 156 | unsigned long length; |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 157 | |
Julius Werner | 9d0cc2a | 2020-01-22 18:00:18 -0800 | [diff] [blame] | 158 | ct = get_cmos_layout(); |
| 159 | if (!ct) |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 160 | return CB_CMOS_LAYOUT_NOT_FOUND; |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 161 | |
Angel Pons | 109a9ec | 2021-04-23 12:58:42 +0200 | [diff] [blame^] | 162 | ce = find_cmos_entry(ct, name); |
| 163 | if (!ce) { |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 164 | printk(BIOS_DEBUG, "WARNING: No CMOS option '%s'.\n", name); |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 165 | return CB_CMOS_OPTION_NOT_FOUND; |
| 166 | } |
| 167 | |
| 168 | length = ce->length; |
| 169 | if (ce->config == 's') { |
| 170 | length = MAX(strlen((const char *)value) * 8, ce->length - 8); |
| 171 | /* make sure the string is null terminated */ |
| 172 | if (set_cmos_value(ce->bit + ce->length - 8, 8, &(u8[]){0}) |
Julius Werner | 9d0cc2a | 2020-01-22 18:00:18 -0800 | [diff] [blame] | 173 | != CB_SUCCESS) |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 174 | return CB_CMOS_ACCESS_ERROR; |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 175 | } |
| 176 | |
Julius Werner | 9d0cc2a | 2020-01-22 18:00:18 -0800 | [diff] [blame] | 177 | if (set_cmos_value(ce->bit, length, value) != CB_SUCCESS) |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 178 | return CB_CMOS_ACCESS_ERROR; |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 179 | |
Kyösti Mälkki | 034cf63 | 2020-01-05 08:12:00 +0200 | [diff] [blame] | 180 | return CB_SUCCESS; |
| 181 | } |
Kyösti Mälkki | b2680a1 | 2020-01-04 18:04:39 +0200 | [diff] [blame] | 182 | |
| 183 | int cmos_lb_cks_valid(void) |
| 184 | { |
| 185 | return cmos_checksum_valid(LB_CKS_RANGE_START, LB_CKS_RANGE_END, LB_CKS_LOC); |
| 186 | } |
| 187 | |
Kyösti Mälkki | b2680a1 | 2020-01-04 18:04:39 +0200 | [diff] [blame] | 188 | void sanitize_cmos(void) |
| 189 | { |
Bill XIE | 51ce41c | 2020-03-29 21:40:04 +0800 | [diff] [blame] | 190 | const unsigned char *cmos_default; |
| 191 | const bool cmos_need_reset = |
| 192 | CONFIG(STATIC_OPTION_TABLE) || cmos_error() || !cmos_lb_cks_valid(); |
| 193 | size_t length = 128; |
| 194 | size_t i; |
| 195 | |
| 196 | if (CONFIG(TPM_MEASURED_BOOT) || cmos_need_reset) { |
Julius Werner | 834b3ec | 2020-03-04 16:52:08 -0800 | [diff] [blame] | 197 | cmos_default = cbfs_map("cmos.default", &length); |
Bill XIE | 51ce41c | 2020-03-29 21:40:04 +0800 | [diff] [blame] | 198 | |
| 199 | if (!cmos_default || !cmos_need_reset) |
| 200 | return; |
| 201 | |
| 202 | u8 control_state = cmos_disable_rtc(); |
| 203 | for (i = 14; i < MIN(128, length); i++) |
| 204 | cmos_write_inner(cmos_default[i], i); |
| 205 | cmos_restore_rtc(control_state); |
| 206 | } |
Kyösti Mälkki | b2680a1 | 2020-01-04 18:04:39 +0200 | [diff] [blame] | 207 | } |