|
|
1.1 ! root 1: // Coreboot interface support. ! 2: // ! 3: // Copyright (C) 2008,2009 Kevin O'Connor <[email protected]> ! 4: // ! 5: // This file may be distributed under the terms of the GNU LGPLv3 license. ! 6: ! 7: #include "memmap.h" // add_e820 ! 8: #include "util.h" // dprintf ! 9: #include "pci.h" // struct pir_header ! 10: #include "acpi.h" // struct rsdp_descriptor ! 11: #include "mptable.h" // MPTABLE_SIGNATURE ! 12: #include "biosvar.h" // GET_EBDA ! 13: #include "lzmadecode.h" // LzmaDecode ! 14: #include "smbios.h" // smbios_init ! 15: ! 16: ! 17: /**************************************************************** ! 18: * Memory map ! 19: ****************************************************************/ ! 20: ! 21: struct cb_header { ! 22: u32 signature; ! 23: u32 header_bytes; ! 24: u32 header_checksum; ! 25: u32 table_bytes; ! 26: u32 table_checksum; ! 27: u32 table_entries; ! 28: }; ! 29: ! 30: #define CB_SIGNATURE 0x4f49424C // "LBIO" ! 31: ! 32: struct cb_memory_range { ! 33: u64 start; ! 34: u64 size; ! 35: u32 type; ! 36: }; ! 37: ! 38: #define CB_MEM_TABLE 16 ! 39: ! 40: struct cb_memory { ! 41: u32 tag; ! 42: u32 size; ! 43: struct cb_memory_range map[0]; ! 44: }; ! 45: ! 46: #define CB_TAG_MEMORY 0x01 ! 47: ! 48: #define MEM_RANGE_COUNT(_rec) \ ! 49: (((_rec)->size - sizeof(*(_rec))) / sizeof((_rec)->map[0])) ! 50: ! 51: struct cb_mainboard { ! 52: u32 tag; ! 53: u32 size; ! 54: u8 vendor_idx; ! 55: u8 part_idx; ! 56: char strings[0]; ! 57: }; ! 58: ! 59: #define CB_TAG_MAINBOARD 0x0003 ! 60: ! 61: struct cb_forward { ! 62: u32 tag; ! 63: u32 size; ! 64: u64 forward; ! 65: }; ! 66: ! 67: #define CB_TAG_FORWARD 0x11 ! 68: ! 69: static u16 ! 70: ipchksum(char *buf, int count) ! 71: { ! 72: u16 *p = (u16*)buf; ! 73: u32 sum = 0; ! 74: while (count > 1) { ! 75: sum += *p++; ! 76: count -= 2; ! 77: } ! 78: if (count) ! 79: sum += *(u8*)p; ! 80: sum = (sum >> 16) + (sum & 0xffff); ! 81: sum += (sum >> 16); ! 82: return ~sum; ! 83: } ! 84: ! 85: // Try to locate the coreboot header in a given address range. ! 86: static struct cb_header * ! 87: find_cb_header(char *addr, int len) ! 88: { ! 89: char *end = addr + len; ! 90: for (; addr < end; addr += 16) { ! 91: struct cb_header *cbh = (struct cb_header *)addr; ! 92: if (cbh->signature != CB_SIGNATURE) ! 93: continue; ! 94: if (! cbh->table_bytes) ! 95: continue; ! 96: if (ipchksum(addr, sizeof(*cbh)) != 0) ! 97: continue; ! 98: if (ipchksum(addr + sizeof(*cbh), cbh->table_bytes) ! 99: != cbh->table_checksum) ! 100: continue; ! 101: return cbh; ! 102: } ! 103: return NULL; ! 104: } ! 105: ! 106: // Try to find the coreboot memory table in the given coreboot table. ! 107: static void * ! 108: find_cb_subtable(struct cb_header *cbh, u32 tag) ! 109: { ! 110: char *tbl = (char *)cbh + sizeof(*cbh); ! 111: int i; ! 112: for (i=0; i<cbh->table_entries; i++) { ! 113: struct cb_memory *cbm = (struct cb_memory *)tbl; ! 114: tbl += cbm->size; ! 115: if (cbm->tag == tag) ! 116: return cbm; ! 117: } ! 118: return NULL; ! 119: } ! 120: ! 121: static struct cb_memory *CBMemTable; ! 122: ! 123: // Populate max ram and e820 map info by scanning for a coreboot table. ! 124: static void ! 125: coreboot_fill_map() ! 126: { ! 127: dprintf(3, "Attempting to find coreboot table\n"); ! 128: ! 129: CBMemTable = NULL; ! 130: ! 131: // Find coreboot table. ! 132: struct cb_header *cbh = find_cb_header(0, 0x1000); ! 133: if (!cbh) ! 134: goto fail; ! 135: struct cb_forward *cbf = find_cb_subtable(cbh, CB_TAG_FORWARD); ! 136: if (cbf) { ! 137: dprintf(3, "Found coreboot table forwarder.\n"); ! 138: cbh = find_cb_header((char *)((u32)cbf->forward), 0x100); ! 139: if (!cbh) ! 140: goto fail; ! 141: } ! 142: dprintf(3, "Now attempting to find coreboot memory map\n"); ! 143: struct cb_memory *cbm = CBMemTable = find_cb_subtable(cbh, CB_TAG_MEMORY); ! 144: if (!cbm) ! 145: goto fail; ! 146: ! 147: u64 maxram = 0, maxram_over4G = 0; ! 148: int i, count = MEM_RANGE_COUNT(cbm); ! 149: for (i=0; i<count; i++) { ! 150: struct cb_memory_range *m = &cbm->map[i]; ! 151: u32 type = m->type; ! 152: if (type == CB_MEM_TABLE) { ! 153: type = E820_RESERVED; ! 154: } else if (type == E820_ACPI || type == E820_RAM) { ! 155: u64 end = m->start + m->size; ! 156: if (end > 0x100000000ull) { ! 157: end -= 0x100000000ull; ! 158: if (end > maxram_over4G) ! 159: maxram_over4G = end; ! 160: } else if (end > maxram) ! 161: maxram = end; ! 162: } ! 163: add_e820(m->start, m->size, type); ! 164: } ! 165: ! 166: RamSize = maxram; ! 167: RamSizeOver4G = maxram_over4G; ! 168: ! 169: // Ughh - coreboot likes to set a map at 0x0000-0x1000, but this ! 170: // confuses grub. So, override it. ! 171: add_e820(0, 16*1024, E820_RAM); ! 172: ! 173: struct cb_mainboard *cbmb = find_cb_subtable(cbh, CB_TAG_MAINBOARD); ! 174: if (cbmb) { ! 175: const char *vendor = &cbmb->strings[cbmb->vendor_idx]; ! 176: const char *part = &cbmb->strings[cbmb->part_idx]; ! 177: dprintf(1, "Found mainboard %s %s\n", vendor, part); ! 178: ! 179: vgahook_setup(vendor, part); ! 180: } ! 181: ! 182: return; ! 183: ! 184: fail: ! 185: // No table found.. Use 16Megs as a dummy value. ! 186: dprintf(1, "Unable to find coreboot table!\n"); ! 187: RamSize = 16*1024*1024; ! 188: RamSizeOver4G = 0; ! 189: add_e820(0, 16*1024*1024, E820_RAM); ! 190: return; ! 191: } ! 192: ! 193: ! 194: /**************************************************************** ! 195: * BIOS table copying ! 196: ****************************************************************/ ! 197: ! 198: static void ! 199: copy_pir(void *pos) ! 200: { ! 201: struct pir_header *p = pos; ! 202: if (p->signature != PIR_SIGNATURE) ! 203: return; ! 204: if (PirOffset) ! 205: return; ! 206: if (p->size < sizeof(*p)) ! 207: return; ! 208: if (checksum(pos, p->size) != 0) ! 209: return; ! 210: void *newpos = malloc_fseg(p->size); ! 211: if (!newpos) { ! 212: dprintf(1, "No room to copy PIR table!\n"); ! 213: return; ! 214: } ! 215: dprintf(1, "Copying PIR from %p to %p\n", pos, newpos); ! 216: memcpy(newpos, pos, p->size); ! 217: PirOffset = (u32)newpos - BUILD_BIOS_ADDR; ! 218: } ! 219: ! 220: static void ! 221: copy_mptable(void *pos) ! 222: { ! 223: struct mptable_floating_s *p = pos; ! 224: if (p->signature != MPTABLE_SIGNATURE) ! 225: return; ! 226: if (!p->physaddr) ! 227: return; ! 228: if (checksum(pos, sizeof(*p)) != 0) ! 229: return; ! 230: u32 length = p->length * 16; ! 231: u16 mpclength = ((struct mptable_config_s *)p->physaddr)->length; ! 232: struct mptable_floating_s *newpos = malloc_fseg(length + mpclength); ! 233: if (!newpos) { ! 234: dprintf(1, "No room to copy MPTABLE!\n"); ! 235: return; ! 236: } ! 237: dprintf(1, "Copying MPTABLE from %p/%x to %p\n", pos, p->physaddr, newpos); ! 238: memcpy(newpos, pos, length); ! 239: newpos->physaddr = (u32)newpos + length; ! 240: newpos->checksum -= checksum(newpos, sizeof(*newpos)); ! 241: memcpy((void*)newpos + length, (void*)p->physaddr, mpclength); ! 242: } ! 243: ! 244: static void ! 245: copy_acpi_rsdp(void *pos) ! 246: { ! 247: if (RsdpAddr) ! 248: return; ! 249: struct rsdp_descriptor *p = pos; ! 250: if (p->signature != RSDP_SIGNATURE) ! 251: return; ! 252: u32 length = 20; ! 253: if (checksum(pos, length) != 0) ! 254: return; ! 255: if (p->revision > 1) { ! 256: length = p->length; ! 257: if (checksum(pos, length) != 0) ! 258: return; ! 259: } ! 260: void *newpos = malloc_fseg(length); ! 261: if (!newpos) { ! 262: dprintf(1, "No room to copy ACPI RSDP table!\n"); ! 263: return; ! 264: } ! 265: dprintf(1, "Copying ACPI RSDP from %p to %p\n", pos, newpos); ! 266: memcpy(newpos, pos, length); ! 267: RsdpAddr = newpos; ! 268: } ! 269: ! 270: // Attempt to find (and relocate) any standard bios tables found in a ! 271: // given address range. ! 272: static void ! 273: scan_tables(u32 start, u32 size) ! 274: { ! 275: void *p = (void*)ALIGN(start, 16); ! 276: void *end = (void*)start + size; ! 277: for (; p<end; p += 16) { ! 278: copy_pir(p); ! 279: copy_mptable(p); ! 280: copy_acpi_rsdp(p); ! 281: } ! 282: } ! 283: ! 284: void ! 285: coreboot_copy_biostable() ! 286: { ! 287: struct cb_memory *cbm = CBMemTable; ! 288: if (! CONFIG_COREBOOT || !cbm) ! 289: return; ! 290: ! 291: dprintf(3, "Relocating coreboot bios tables\n"); ! 292: ! 293: // Init variables set in coreboot table memory scan. ! 294: PirOffset = 0; ! 295: RsdpAddr = 0; ! 296: ! 297: // Scan CB_MEM_TABLE areas for bios tables. ! 298: int i, count = MEM_RANGE_COUNT(cbm); ! 299: for (i=0; i<count; i++) { ! 300: struct cb_memory_range *m = &cbm->map[i]; ! 301: if (m->type == CB_MEM_TABLE) ! 302: scan_tables(m->start, m->size); ! 303: } ! 304: ! 305: // XXX - just create dummy smbios table for now - should detect if ! 306: // smbios/dmi table is found from coreboot and use that instead. ! 307: smbios_init(); ! 308: } ! 309: ! 310: ! 311: /**************************************************************** ! 312: * ulzma ! 313: ****************************************************************/ ! 314: ! 315: // Uncompress data in flash to an area of memory. ! 316: static int ! 317: ulzma(u8 *dst, u32 maxlen, const u8 *src, u32 srclen) ! 318: { ! 319: dprintf(3, "Uncompressing data %d@%p to %d@%p\n", srclen, src, maxlen, dst); ! 320: CLzmaDecoderState state; ! 321: int ret = LzmaDecodeProperties(&state.Properties, src, LZMA_PROPERTIES_SIZE); ! 322: if (ret != LZMA_RESULT_OK) { ! 323: dprintf(1, "LzmaDecodeProperties error - %d\n", ret); ! 324: return -1; ! 325: } ! 326: u8 scratch[15980]; ! 327: int need = (LzmaGetNumProbs(&state.Properties) * sizeof(CProb)); ! 328: if (need > sizeof(scratch)) { ! 329: dprintf(1, "LzmaDecode need %d have %d\n", need, sizeof(scratch)); ! 330: return -1; ! 331: } ! 332: state.Probs = (CProb *)scratch; ! 333: ! 334: u32 dstlen = *(u32*)(src + LZMA_PROPERTIES_SIZE); ! 335: if (dstlen > maxlen) { ! 336: dprintf(1, "LzmaDecode too large (max %d need %d)\n", maxlen, dstlen); ! 337: return -1; ! 338: } ! 339: u32 inProcessed, outProcessed; ! 340: ret = LzmaDecode(&state, src + LZMA_PROPERTIES_SIZE + 8, srclen ! 341: , &inProcessed, dst, dstlen, &outProcessed); ! 342: if (ret) { ! 343: dprintf(1, "LzmaDecode returned %d\n", ret); ! 344: return -1; ! 345: } ! 346: return dstlen; ! 347: } ! 348: ! 349: ! 350: /**************************************************************** ! 351: * Coreboot flash format ! 352: ****************************************************************/ ! 353: ! 354: #define CBFS_HEADER_MAGIC 0x4F524243 ! 355: #define CBFS_HEADPTR_ADDR 0xFFFFFFFc ! 356: #define CBFS_VERSION1 0x31313131 ! 357: ! 358: struct cbfs_header { ! 359: u32 magic; ! 360: u32 version; ! 361: u32 romsize; ! 362: u32 bootblocksize; ! 363: u32 align; ! 364: u32 offset; ! 365: u32 pad[2]; ! 366: } PACKED; ! 367: ! 368: static struct cbfs_header *CBHDR; ! 369: ! 370: static void ! 371: cbfs_setup() ! 372: { ! 373: if (! CONFIG_COREBOOT_FLASH) ! 374: return; ! 375: ! 376: CBHDR = *(void **)CBFS_HEADPTR_ADDR; ! 377: if (CBHDR->magic != htonl(CBFS_HEADER_MAGIC)) { ! 378: dprintf(1, "Unable to find CBFS (got %x not %x)\n" ! 379: , CBHDR->magic, htonl(CBFS_HEADER_MAGIC)); ! 380: CBHDR = NULL; ! 381: return; ! 382: } ! 383: ! 384: dprintf(1, "Found CBFS header at %p\n", CBHDR); ! 385: } ! 386: ! 387: #define CBFS_FILE_MAGIC 0x455649484352414cLL // LARCHIVE ! 388: ! 389: struct cbfs_file { ! 390: u64 magic; ! 391: u32 len; ! 392: u32 type; ! 393: u32 checksum; ! 394: u32 offset; ! 395: char filename[0]; ! 396: } PACKED; ! 397: ! 398: // Verify a cbfs entry looks valid. ! 399: static struct cbfs_file * ! 400: cbfs_verify(struct cbfs_file *file) ! 401: { ! 402: if (file < (struct cbfs_file *)(0xFFFFFFFF - ntohl(CBHDR->romsize))) ! 403: return NULL; ! 404: u64 magic = file->magic; ! 405: if (magic == CBFS_FILE_MAGIC) { ! 406: dprintf(5, "Found CBFS file %s\n", file->filename); ! 407: return file; ! 408: } ! 409: return NULL; ! 410: } ! 411: ! 412: // Return the first file in the CBFS archive ! 413: static struct cbfs_file * ! 414: cbfs_getfirst() ! 415: { ! 416: if (! CBHDR) ! 417: return NULL; ! 418: return cbfs_verify((void *)(0 - ntohl(CBHDR->romsize) + ntohl(CBHDR->offset))); ! 419: } ! 420: ! 421: // Return the file after the given file. ! 422: static struct cbfs_file * ! 423: cbfs_getnext(struct cbfs_file *file) ! 424: { ! 425: file = (void*)file + ALIGN(ntohl(file->len) + ntohl(file->offset), ntohl(CBHDR->align)); ! 426: return cbfs_verify(file); ! 427: } ! 428: ! 429: // Find the file with the given filename. ! 430: struct cbfs_file * ! 431: cbfs_findfile(const char *fname) ! 432: { ! 433: dprintf(3, "Searching CBFS for %s\n", fname); ! 434: struct cbfs_file *file; ! 435: for (file = cbfs_getfirst(); file; file = cbfs_getnext(file)) ! 436: if (strcmp(fname, file->filename) == 0) ! 437: return file; ! 438: return NULL; ! 439: } ! 440: ! 441: // Find next file with the given filename prefix. ! 442: struct cbfs_file * ! 443: cbfs_findprefix(const char *prefix, struct cbfs_file *last) ! 444: { ! 445: if (! CONFIG_COREBOOT_FLASH) ! 446: return NULL; ! 447: ! 448: dprintf(3, "Searching CBFS for prefix %s\n", prefix); ! 449: int len = strlen(prefix); ! 450: struct cbfs_file *file; ! 451: if (! last) ! 452: file = cbfs_getfirst(); ! 453: else ! 454: file = cbfs_getnext(last); ! 455: for (; file; file = cbfs_getnext(file)) ! 456: if (memcmp(prefix, file->filename, len) == 0) ! 457: return file; ! 458: return NULL; ! 459: } ! 460: ! 461: // Find a file with the given filename (possibly with ".lzma" extension). ! 462: static struct cbfs_file * ! 463: cbfs_finddatafile(const char *fname) ! 464: { ! 465: int fnlen = strlen(fname); ! 466: struct cbfs_file *file = NULL; ! 467: for (;;) { ! 468: file = cbfs_findprefix(fname, file); ! 469: if (!file) ! 470: return NULL; ! 471: if (file->filename[fnlen] == '\0' ! 472: || strcmp(&file->filename[fnlen], ".lzma") == 0) ! 473: return file; ! 474: } ! 475: } ! 476: ! 477: // Determine whether the file has a ".lzma" extension. ! 478: static int ! 479: cbfs_iscomp(struct cbfs_file *file) ! 480: { ! 481: int fnamelen = strlen(file->filename); ! 482: return fnamelen > 5 && strcmp(&file->filename[fnamelen-5], ".lzma") == 0; ! 483: } ! 484: ! 485: // Return the filename of a given file. ! 486: const char * ! 487: cbfs_filename(struct cbfs_file *file) ! 488: { ! 489: return file->filename; ! 490: } ! 491: ! 492: // Determine the uncompressed size of a datafile. ! 493: u32 ! 494: cbfs_datasize(struct cbfs_file *file) ! 495: { ! 496: void *src = (void*)file + ntohl(file->offset); ! 497: if (cbfs_iscomp(file)) ! 498: return *(u32*)(src + LZMA_PROPERTIES_SIZE); ! 499: return ntohl(file->len); ! 500: } ! 501: ! 502: // Copy a file to memory (uncompressing if necessary) ! 503: int ! 504: cbfs_copyfile(struct cbfs_file *file, void *dst, u32 maxlen) ! 505: { ! 506: if (! CONFIG_COREBOOT_FLASH || !file) ! 507: return -1; ! 508: ! 509: u32 size = ntohl(file->len); ! 510: void *src = (void*)file + ntohl(file->offset); ! 511: if (cbfs_iscomp(file)) { ! 512: // Compressed - copy to temp ram and uncompress it. ! 513: u32 asize = ALIGN(size, 4); ! 514: void *temp = malloc_tmphigh(asize); ! 515: if (!temp) ! 516: return -1; ! 517: iomemcpy(temp, src, asize); ! 518: int ret = ulzma(dst, maxlen, temp, size); ! 519: yield(); ! 520: free(temp); ! 521: return ret; ! 522: } ! 523: ! 524: // Not compressed. ! 525: dprintf(3, "Copying data %d@%p to %d@%p\n", size, src, maxlen, dst); ! 526: if (size > maxlen) { ! 527: dprintf(1, "File too big to copy\n"); ! 528: return -1; ! 529: } ! 530: iomemcpy(dst, src, size); ! 531: return size; ! 532: } ! 533: ! 534: // Find and copy the optionrom for the given vendor/device id. ! 535: int ! 536: cbfs_copy_optionrom(void *dst, u32 maxlen, u32 vendev) ! 537: { ! 538: if (! CONFIG_COREBOOT_FLASH) ! 539: return -1; ! 540: ! 541: char fname[17]; ! 542: snprintf(fname, sizeof(fname), "pci%04x,%04x.rom" ! 543: , (u16)vendev, vendev >> 16); ! 544: return cbfs_copyfile(cbfs_finddatafile(fname), dst, maxlen); ! 545: } ! 546: ! 547: struct cbfs_payload_segment { ! 548: u32 type; ! 549: u32 compression; ! 550: u32 offset; ! 551: u64 load_addr; ! 552: u32 len; ! 553: u32 mem_len; ! 554: } PACKED; ! 555: ! 556: #define PAYLOAD_SEGMENT_BSS 0x20535342 ! 557: #define PAYLOAD_SEGMENT_ENTRY 0x52544E45 ! 558: ! 559: #define CBFS_COMPRESS_NONE 0 ! 560: #define CBFS_COMPRESS_LZMA 1 ! 561: ! 562: struct cbfs_payload { ! 563: struct cbfs_payload_segment segments[1]; ! 564: }; ! 565: ! 566: void ! 567: cbfs_run_payload(struct cbfs_file *file) ! 568: { ! 569: if (!CONFIG_COREBOOT_FLASH || !file) ! 570: return; ! 571: dprintf(1, "Run %s\n", file->filename); ! 572: struct cbfs_payload *pay = (void*)file + ntohl(file->offset); ! 573: struct cbfs_payload_segment *seg = pay->segments; ! 574: for (;;) { ! 575: void *src = (void*)pay + ntohl(seg->offset); ! 576: void *dest = (void*)ntohl((u32)seg->load_addr); ! 577: u32 src_len = ntohl(seg->len); ! 578: u32 dest_len = ntohl(seg->mem_len); ! 579: switch (seg->type) { ! 580: case PAYLOAD_SEGMENT_BSS: ! 581: dprintf(3, "BSS segment %d@%p\n", dest_len, dest); ! 582: memset(dest, 0, dest_len); ! 583: break; ! 584: case PAYLOAD_SEGMENT_ENTRY: { ! 585: dprintf(1, "Calling addr %p\n", dest); ! 586: void (*func)() = dest; ! 587: func(); ! 588: return; ! 589: } ! 590: default: ! 591: dprintf(3, "Segment %x %d@%p -> %d@%p\n" ! 592: , seg->type, src_len, src, dest_len, dest); ! 593: if (seg->compression == htonl(CBFS_COMPRESS_NONE)) { ! 594: if (src_len > dest_len) ! 595: src_len = dest_len; ! 596: memcpy(dest, src, src_len); ! 597: } else if (CONFIG_LZMA ! 598: && seg->compression == htonl(CBFS_COMPRESS_LZMA)) { ! 599: int ret = ulzma(dest, dest_len, src, src_len); ! 600: if (ret < 0) ! 601: return; ! 602: src_len = ret; ! 603: } else { ! 604: dprintf(1, "No support for compression type %x\n" ! 605: , seg->compression); ! 606: return; ! 607: } ! 608: if (dest_len > src_len) ! 609: memset(dest + src_len, 0, dest_len - src_len); ! 610: break; ! 611: } ! 612: seg++; ! 613: } ! 614: } ! 615: ! 616: void ! 617: coreboot_setup(void) ! 618: { ! 619: coreboot_fill_map(); ! 620: cbfs_setup(); ! 621: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.