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