layout.c
1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 3 #include "common.h" 4 #include "layout.h" 5 #include "cmos_lowlevel.h" 6 7 typedef struct cmos_entry_item_t cmos_entry_item_t; 8 9 struct cmos_entry_item_t { 10 cmos_entry_t item; 11 cmos_entry_item_t *next; 12 }; 13 14 typedef struct cmos_enum_item_t cmos_enum_item_t; 15 16 struct cmos_enum_item_t { 17 cmos_enum_t item; 18 cmos_enum_item_t *next; 19 }; 20 21 static void default_cmos_layout_get_fn(void); 22 static int areas_overlap(unsigned area_0_start, unsigned area_0_length, 23 unsigned area_1_start, unsigned area_1_length); 24 static int entries_overlap(const cmos_entry_t * p, const cmos_entry_t * q); 25 static const cmos_enum_item_t *find_first_cmos_enum_id(unsigned config_id); 26 27 const char checksum_param_name[] = "check_sum"; 28 29 /* Newer versions of coreboot store the 3 pieces of information below in the 30 * coreboot table so we don't have to rely on hardcoded values. 31 */ 32 33 /* This is the offset from the start of CMOS of the first byte that the 34 * checksum is calculated over. 35 */ 36 #define CMOS_CHECKSUM_START 49 37 38 /* This is the offset from the start of CMOS of the last byte that the 39 * checksum is calculated over. 40 */ 41 #define CMOS_CHECKSUM_END 125 42 43 /* This is the offset from the start of CMOS where the coreboot checksum is 44 * stored. 45 */ 46 #define CMOS_CHECKSUM_INDEX 126 47 48 /* index of first byte of checksummed area */ 49 unsigned cmos_checksum_start = CMOS_CHECKSUM_START; 50 51 /* index of last byte of checksummed area */ 52 unsigned cmos_checksum_end = CMOS_CHECKSUM_END; 53 54 /* index of first byte of CMOS checksum (a big-endian 16-bit value) */ 55 unsigned cmos_checksum_index = CMOS_CHECKSUM_INDEX; 56 57 /* List is sorted in ascending order according to 'bit' field in 58 * cmos_entry_t. 59 */ 60 static cmos_entry_item_t *cmos_entry_list = NULL; 61 62 /* List is sorted in ascending order: first by 'config_id' and then by 63 * 'value'. 64 */ 65 static cmos_enum_item_t *cmos_enum_list = NULL; 66 67 static cmos_layout_get_fn_t cmos_layout_get_fn = default_cmos_layout_get_fn; 68 69 /**************************************************************************** 70 * entries_overlap 71 * 72 * Return 1 if CMOS entries 'p' and 'q' overlap. Else return 0. 73 ****************************************************************************/ 74 static inline int entries_overlap(const cmos_entry_t * p, 75 const cmos_entry_t * q) 76 { 77 return areas_overlap(p->bit, p->length, q->bit, q->length); 78 } 79 80 /**************************************************************************** 81 * cmos_entry_to_const_item 82 * 83 * Return a pointer to the cmos_entry_item_t that 'p' is embedded within. 84 ****************************************************************************/ 85 static inline const cmos_entry_item_t *cmos_entry_to_const_item 86 (const cmos_entry_t * p) { 87 static const cmos_entry_t *pos = &((cmos_entry_item_t *) 0)->item; 88 unsigned long offset, address; 89 90 offset = (unsigned long)pos; 91 address = ((unsigned long)p) - offset; 92 return (const cmos_entry_item_t *)address; 93 } 94 95 /**************************************************************************** 96 * cmos_enum_to_const_item 97 * 98 * Return a pointer to the cmos_enum_item_t that 'p' is embedded within. 99 ****************************************************************************/ 100 static inline const cmos_enum_item_t *cmos_enum_to_const_item 101 (const cmos_enum_t * p) { 102 static const cmos_enum_t *pos = &((cmos_enum_item_t *) 0)->item; 103 unsigned long offset, address; 104 105 offset = (unsigned long)pos; 106 address = ((unsigned long)p) - offset; 107 return (const cmos_enum_item_t *)address; 108 } 109 110 /**************************************************************************** 111 * register_cmos_layout_get_fn 112 * 113 * Set 'fn' as the function that will be called to retrieve CMOS layout 114 * information. 115 ****************************************************************************/ 116 void register_cmos_layout_get_fn(cmos_layout_get_fn_t fn) 117 { 118 cmos_layout_get_fn = fn; 119 } 120 121 /**************************************************************************** 122 * get_cmos_layout 123 * 124 * Retrieve CMOS layout information and store it in our internal repository. 125 ****************************************************************************/ 126 void get_cmos_layout(void) 127 { 128 cmos_layout_get_fn(); 129 } 130 131 /**************************************************************************** 132 * add_cmos_entry 133 * 134 * Attempt to add CMOS entry 'e' to our internal repository of layout 135 * information. Return OK on success or an error code on failure. If 136 * operation fails because 'e' overlaps an existing CMOS entry, '*conflict' 137 * will be set to point to the overlapping entry. 138 ****************************************************************************/ 139 int add_cmos_entry(const cmos_entry_t * e, const cmos_entry_t ** conflict) 140 { 141 cmos_entry_item_t *item, *prev, *new_entry; 142 143 *conflict = NULL; 144 145 if (e->length < 1) 146 return LAYOUT_ENTRY_BAD_LENGTH; 147 148 if (e->bit % 8 && e->bit / 8 != (e->bit + e->length - 1) / 8) 149 return LAYOUT_MULTIBYTE_ENTRY_NOT_ALIGNED; 150 151 if ((new_entry = 152 (cmos_entry_item_t *) malloc(sizeof(*new_entry))) == NULL) 153 out_of_memory(); 154 155 new_entry->item = *e; 156 157 if (cmos_entry_list == NULL) { 158 new_entry->next = NULL; 159 cmos_entry_list = new_entry; 160 return OK; 161 } 162 163 /* Find place in list to insert new entry. List is sorted in ascending 164 * order. 165 */ 166 for (item = cmos_entry_list, prev = NULL; 167 (item != NULL) && (item->item.bit < e->bit); 168 prev = item, item = item->next) ; 169 170 if (prev == NULL) { 171 if (entries_overlap(e, &cmos_entry_list->item)) { 172 *conflict = &cmos_entry_list->item; 173 goto fail; 174 } 175 176 new_entry->next = cmos_entry_list; 177 cmos_entry_list = new_entry; 178 return OK; 179 } 180 181 if (entries_overlap(&prev->item, e)) { 182 *conflict = &prev->item; 183 goto fail; 184 } 185 186 if ((item != NULL) && entries_overlap(e, &item->item)) { 187 *conflict = &item->item; 188 goto fail; 189 } 190 191 new_entry->next = item; 192 prev->next = new_entry; 193 return OK; 194 195 fail: 196 free(new_entry); 197 return LAYOUT_ENTRY_OVERLAP; 198 } 199 200 /**************************************************************************** 201 * find_cmos_entry 202 * 203 * Search for a CMOS entry whose name is 'name'. Return pointer to matching 204 * entry or NULL if entry not found. 205 ****************************************************************************/ 206 const cmos_entry_t *find_cmos_entry(const char name[]) 207 { 208 cmos_entry_item_t *item; 209 210 for (item = cmos_entry_list; item != NULL; item = item->next) { 211 if (!strcmp(item->item.name, name)) 212 return &item->item; 213 } 214 215 return NULL; 216 } 217 218 /**************************************************************************** 219 * first_cmos_entry 220 * 221 * Return a pointer to the first CMOS entry in our list or NULL if list is 222 * empty. 223 ****************************************************************************/ 224 const cmos_entry_t *first_cmos_entry(void) 225 { 226 return (cmos_entry_list == NULL) ? NULL : &cmos_entry_list->item; 227 } 228 229 /**************************************************************************** 230 * next_cmos_entry 231 * 232 * Return a pointer to next entry in list after 'last' or NULL if no more 233 * entries. 234 ****************************************************************************/ 235 const cmos_entry_t *next_cmos_entry(const cmos_entry_t * last) 236 { 237 const cmos_entry_item_t *last_item, *next_item; 238 239 last_item = cmos_entry_to_const_item(last); 240 next_item = last_item->next; 241 return (next_item == NULL) ? NULL : &next_item->item; 242 } 243 244 /**************************************************************************** 245 * add_cmos_enum 246 * 247 * Attempt to add CMOS enum 'e' to our internal repository of layout 248 * information. Return OK on success or an error code on failure. 249 ****************************************************************************/ 250 int add_cmos_enum(const cmos_enum_t * e) 251 { 252 cmos_enum_item_t *item, *prev, *new_enum; 253 254 if ((new_enum = (cmos_enum_item_t *) malloc(sizeof(*new_enum))) == NULL) 255 out_of_memory(); 256 257 new_enum->item = *e; 258 259 if (cmos_enum_list == NULL) { 260 new_enum->next = NULL; 261 cmos_enum_list = new_enum; 262 return OK; 263 } 264 265 /* The list of enums is sorted in ascending order, first by 266 * 'config_id' and then by 'value'. Look for the first enum 267 * whose 'config_id' field matches 'e'. 268 */ 269 for (item = cmos_enum_list, prev = NULL; 270 (item != NULL) && (item->item.config_id < e->config_id); 271 prev = item, item = item->next) ; 272 273 if (item == NULL) { 274 new_enum->next = NULL; 275 prev->next = new_enum; 276 return OK; 277 } 278 279 if (item->item.config_id > e->config_id) { 280 new_enum->next = item; 281 282 if (prev == NULL) 283 cmos_enum_list = new_enum; 284 else 285 prev->next = new_enum; 286 287 return OK; 288 } 289 290 /* List already contains at least one enum whose 'config_id' 291 * matches 'e'. Now find proper place to insert 'e' based on 292 * 'value'. 293 */ 294 while (item->item.value < e->value) { 295 prev = item; 296 item = item->next; 297 298 if ((item == NULL) || (item->item.config_id != e->config_id)) { 299 new_enum->next = item; 300 prev->next = new_enum; 301 return OK; 302 } 303 } 304 305 if (item->item.value == e->value) { 306 free(new_enum); 307 return LAYOUT_DUPLICATE_ENUM; 308 } 309 310 new_enum->next = item; 311 312 if (prev == NULL) 313 cmos_enum_list = new_enum; 314 else 315 prev->next = new_enum; 316 317 return OK; 318 } 319 320 /**************************************************************************** 321 * find_cmos_enum 322 * 323 * Search for an enum that matches 'config_id' and 'value'. If found, return 324 * a pointer to the mathcing enum. Else return NULL. 325 ****************************************************************************/ 326 const cmos_enum_t *find_cmos_enum(unsigned config_id, unsigned long long value) 327 { 328 const cmos_enum_item_t *item; 329 330 if ((item = find_first_cmos_enum_id(config_id)) == NULL) 331 return NULL; 332 333 while (item->item.value < value) { 334 item = item->next; 335 336 if ((item == NULL) || (item->item.config_id != config_id)) 337 return NULL; 338 } 339 340 return (item->item.value == value) ? &item->item : NULL; 341 } 342 343 /**************************************************************************** 344 * first_cmos_enum 345 * 346 * Return a pointer to the first CMOS enum in our list or NULL if list is 347 * empty. 348 ****************************************************************************/ 349 const cmos_enum_t *first_cmos_enum(void) 350 { 351 return (cmos_enum_list == NULL) ? NULL : &cmos_enum_list->item; 352 } 353 354 /**************************************************************************** 355 * next_cmos_enum 356 * 357 * Return a pointer to next enum in list after 'last' or NULL if no more 358 * enums. 359 ****************************************************************************/ 360 const cmos_enum_t *next_cmos_enum(const cmos_enum_t * last) 361 { 362 const cmos_enum_item_t *last_item, *next_item; 363 364 last_item = cmos_enum_to_const_item(last); 365 next_item = last_item->next; 366 return (next_item == NULL) ? NULL : &next_item->item; 367 } 368 369 /**************************************************************************** 370 * first_cmos_enum_id 371 * 372 * Return a pointer to the first CMOS enum in our list that matches 373 * 'config_id' or NULL if there are no matching enums. 374 ****************************************************************************/ 375 const cmos_enum_t *first_cmos_enum_id(unsigned config_id) 376 { 377 const cmos_enum_item_t *item; 378 379 item = find_first_cmos_enum_id(config_id); 380 return (item == NULL) ? NULL : &item->item; 381 } 382 383 /**************************************************************************** 384 * next_cmos_enum_id 385 * 386 * Return a pointer to next enum in list after 'last' that matches the 387 * 'config_id' field of 'last' or NULL if there are no more matching enums. 388 ****************************************************************************/ 389 const cmos_enum_t *next_cmos_enum_id(const cmos_enum_t * last) 390 { 391 const cmos_enum_item_t *item; 392 393 item = cmos_enum_to_const_item(last)->next; 394 return ((item == NULL) || (item->item.config_id != last->config_id)) ? 395 NULL : &item->item; 396 } 397 398 /**************************************************************************** 399 * is_checksum_name 400 * 401 * Return 1 if 'name' matches the name of the parameter representing the CMOS 402 * checksum. Else return 0. 403 ****************************************************************************/ 404 int is_checksum_name(const char name[]) 405 { 406 return !strcmp(name, checksum_param_name); 407 } 408 409 /**************************************************************************** 410 * checksum_layout_to_bytes 411 * 412 * On entry, '*layout' contains checksum-related layout information expressed 413 * in bits. Perform sanity checking on the information and convert it from 414 * bit positions to byte positions. Return OK on success or an error code if 415 * a sanity check fails. 416 ****************************************************************************/ 417 int checksum_layout_to_bytes(cmos_checksum_layout_t * layout) 418 { 419 unsigned start, end, index; 420 421 start = layout->summed_area_start; 422 end = layout->summed_area_end; 423 index = layout->checksum_at; 424 425 if (start % 8) 426 return LAYOUT_SUMMED_AREA_START_NOT_ALIGNED; 427 428 if ((end % 8) != 7) 429 return LAYOUT_SUMMED_AREA_END_NOT_ALIGNED; 430 431 if (index % 8) 432 return LAYOUT_CHECKSUM_LOCATION_NOT_ALIGNED; 433 434 if (end <= start) 435 return LAYOUT_INVALID_SUMMED_AREA; 436 437 /* Convert bit positions to byte positions. */ 438 start /= 8; 439 end /= 8; /* equivalent to "end = ((end - 7) / 8)" */ 440 index /= 8; 441 442 if (verify_cmos_byte_index(start) || verify_cmos_byte_index(end)) 443 return LAYOUT_SUMMED_AREA_OUT_OF_RANGE; 444 445 if (verify_cmos_byte_index(index)) 446 return LAYOUT_CHECKSUM_LOCATION_OUT_OF_RANGE; 447 448 /* checksum occupies 16 bits */ 449 if (areas_overlap(start, end - start + 1, index, index + 1)) 450 return LAYOUT_CHECKSUM_OVERLAPS_SUMMED_AREA; 451 452 layout->summed_area_start = start; 453 layout->summed_area_end = end; 454 layout->checksum_at = index; 455 return OK; 456 } 457 458 /**************************************************************************** 459 * checksum_layout_to_bits 460 * 461 * On entry, '*layout' contains checksum-related layout information expressed 462 * in bytes. Convert this information to bit positions. 463 ****************************************************************************/ 464 void checksum_layout_to_bits(cmos_checksum_layout_t * layout) 465 { 466 layout->summed_area_start *= 8; 467 layout->summed_area_end = (layout->summed_area_end * 8) + 7; 468 layout->checksum_at *= 8; 469 } 470 471 /**************************************************************************** 472 * default_cmos_layout_get_fn 473 * 474 * If this function is ever called, it means that an appropriate callback for 475 * obtaining CMOS layout information was not set before attempting to 476 * retrieve layout information. 477 ****************************************************************************/ 478 static void default_cmos_layout_get_fn(void) 479 { 480 BUG(); 481 } 482 483 /**************************************************************************** 484 * areas_overlap 485 * 486 * Return 1 if the two given areas overlap. Else return 0. 487 ****************************************************************************/ 488 static int areas_overlap(unsigned area_0_start, unsigned area_0_length, 489 unsigned area_1_start, unsigned area_1_length) 490 { 491 unsigned area_0_end, area_1_end; 492 493 area_0_end = area_0_start + area_0_length - 1; 494 area_1_end = area_1_start + area_1_length - 1; 495 return ((area_1_start <= area_0_end) && (area_0_start <= area_1_end)); 496 } 497 498 /**************************************************************************** 499 * find_first_cmos_enum_id 500 * 501 * Return a pointer to the first item in our list of enums that matches 502 * 'config_id'. Return NULL if there is no matching enum. 503 ****************************************************************************/ 504 static const cmos_enum_item_t *find_first_cmos_enum_id(unsigned config_id) 505 { 506 cmos_enum_item_t *item; 507 508 for (item = cmos_enum_list; 509 (item != NULL) && (item->item.config_id < config_id); 510 item = item->next) ; 511 512 return ((item == NULL) || (item->item.config_id > config_id)) ? 513 NULL : item; 514 }