blob: 9de99af7c6e552fb9d5015c0ada22f093d8c5a59 [file] [log] [blame]
Kevin O'Connor59a23bb2008-06-08 23:09:42 -04001// Coreboot interface support.
2//
Kevin O'Connor2929c352009-07-25 13:48:27 -04003// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net>
Kevin O'Connor59a23bb2008-06-08 23:09:42 -04004//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05005// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connor59a23bb2008-06-08 23:09:42 -04006
7#include "memmap.h" // add_e820
8#include "util.h" // dprintf
Kevin O'Connor592323f2009-04-26 23:17:49 -04009#include "lzmadecode.h" // LzmaDecode
Kevin O'Connor01a85202009-10-18 09:49:59 -040010#include "smbios.h" // smbios_init
Kevin O'Connor72eee3e2010-12-27 19:07:49 -050011#include "boot.h" // boot_add_cbfs
Kevin O'Connord3140832012-05-13 22:46:12 -040012#include "disk.h" // MAXDESCSIZE
13#include "config.h" // CONFIG_*
Kevin O'Connor59a23bb2008-06-08 23:09:42 -040014
15
16/****************************************************************
Kevin O'Connor93479e42008-06-12 22:22:43 -040017 * Memory map
Kevin O'Connor59a23bb2008-06-08 23:09:42 -040018 ****************************************************************/
19
20struct 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
31struct cb_memory_range {
32 u64 start;
33 u64 size;
34 u32 type;
35};
36
37#define CB_MEM_TABLE 16
38
39struct 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'Connor22e1b912009-07-19 18:52:46 -040050struct 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'Connor071f1542009-03-21 13:17:21 -040060struct cb_forward {
61 u32 tag;
62 u32 size;
63 u64 forward;
64};
65
66#define CB_TAG_FORWARD 0x11
67
Kevin O'Connor59a23bb2008-06-08 23:09:42 -040068static u16
69ipchksum(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.
85static struct cb_header *
86find_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'Connor59a23bb2008-06-08 23:09:42 -040093 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'Connor93479e42008-06-12 22:22:43 -0400106static void *
107find_cb_subtable(struct cb_header *cbh, u32 tag)
Kevin O'Connor59a23bb2008-06-08 23:09:42 -0400108{
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'Connor93479e42008-06-12 22:22:43 -0400114 if (cbm->tag == tag)
Kevin O'Connor59a23bb2008-06-08 23:09:42 -0400115 return cbm;
116 }
117 return NULL;
118}
119
Kevin O'Connordf5a5fd2009-07-28 20:46:03 -0400120static struct cb_memory *CBMemTable;
Julian Pidancet02145152012-02-05 04:51:06 +0000121const char *CBvendor = "", *CBpart = "";
Kevin O'Connordf5a5fd2009-07-28 20:46:03 -0400122
Kevin O'Connor59a23bb2008-06-08 23:09:42 -0400123// Populate max ram and e820 map info by scanning for a coreboot table.
Kevin O'Connor59d6ca52012-05-31 00:20:55 -0400124void
125coreboot_setup(void)
Kevin O'Connor59a23bb2008-06-08 23:09:42 -0400126{
127 dprintf(3, "Attempting to find coreboot table\n");
Kevin O'Connoref3d8822009-02-05 19:51:12 -0500128
Kevin O'Connoref3d8822009-02-05 19:51:12 -0500129 // Find coreboot table.
Kevin O'Connor59a23bb2008-06-08 23:09:42 -0400130 struct cb_header *cbh = find_cb_header(0, 0x1000);
131 if (!cbh)
132 goto fail;
Kevin O'Connor071f1542009-03-21 13:17:21 -0400133 struct cb_forward *cbf = find_cb_subtable(cbh, CB_TAG_FORWARD);
134 if (cbf) {
135 dprintf(3, "Found coreboot table forwarder.\n");
136 cbh = find_cb_header((char *)((u32)cbf->forward), 0x100);
137 if (!cbh)
138 goto fail;
139 }
140 dprintf(3, "Now attempting to find coreboot memory map\n");
Kevin O'Connordf5a5fd2009-07-28 20:46:03 -0400141 struct cb_memory *cbm = CBMemTable = find_cb_subtable(cbh, CB_TAG_MEMORY);
Kevin O'Connor59a23bb2008-06-08 23:09:42 -0400142 if (!cbm)
143 goto fail;
144
Kevin O'Connor59c35f22008-10-25 23:05:42 -0400145 u64 maxram = 0, maxram_over4G = 0;
Kevin O'Connor59a23bb2008-06-08 23:09:42 -0400146 int i, count = MEM_RANGE_COUNT(cbm);
147 for (i=0; i<count; i++) {
148 struct cb_memory_range *m = &cbm->map[i];
149 u32 type = m->type;
Kevin O'Connor93479e42008-06-12 22:22:43 -0400150 if (type == CB_MEM_TABLE) {
Kevin O'Connor59a23bb2008-06-08 23:09:42 -0400151 type = E820_RESERVED;
Kevin O'Connor59c35f22008-10-25 23:05:42 -0400152 } else if (type == E820_ACPI || type == E820_RAM) {
153 u64 end = m->start + m->size;
154 if (end > 0x100000000ull) {
155 end -= 0x100000000ull;
156 if (end > maxram_over4G)
157 maxram_over4G = end;
158 } else if (end > maxram)
159 maxram = end;
Kevin O'Connor93479e42008-06-12 22:22:43 -0400160 }
Kevin O'Connor59a23bb2008-06-08 23:09:42 -0400161 add_e820(m->start, m->size, type);
162 }
163
Kevin O'Connore7916362008-12-28 22:03:17 -0500164 RamSize = maxram;
165 RamSizeOver4G = maxram_over4G;
Kevin O'Connor59c35f22008-10-25 23:05:42 -0400166
Kevin O'Connor59a23bb2008-06-08 23:09:42 -0400167 // Ughh - coreboot likes to set a map at 0x0000-0x1000, but this
168 // confuses grub. So, override it.
169 add_e820(0, 16*1024, E820_RAM);
170
Kevin O'Connor22e1b912009-07-19 18:52:46 -0400171 struct cb_mainboard *cbmb = find_cb_subtable(cbh, CB_TAG_MAINBOARD);
172 if (cbmb) {
Kevin O'Connorc1de91b2011-07-02 13:50:21 -0400173 CBvendor = &cbmb->strings[cbmb->vendor_idx];
174 CBpart = &cbmb->strings[cbmb->part_idx];
175 dprintf(1, "Found mainboard %s %s\n", CBvendor, CBpart);
Kevin O'Connor22e1b912009-07-19 18:52:46 -0400176 }
177
Kevin O'Connor59a23bb2008-06-08 23:09:42 -0400178 return;
179
180fail:
181 // No table found.. Use 16Megs as a dummy value.
182 dprintf(1, "Unable to find coreboot table!\n");
Kevin O'Connore7916362008-12-28 22:03:17 -0500183 RamSize = 16*1024*1024;
184 RamSizeOver4G = 0;
Kevin O'Connor59a23bb2008-06-08 23:09:42 -0400185 add_e820(0, 16*1024*1024, E820_RAM);
186 return;
187}
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400188
189
190/****************************************************************
Kevin O'Connordf5a5fd2009-07-28 20:46:03 -0400191 * BIOS table copying
192 ****************************************************************/
193
Kevin O'Connordf5a5fd2009-07-28 20:46:03 -0400194// Attempt to find (and relocate) any standard bios tables found in a
195// given address range.
196static void
197scan_tables(u32 start, u32 size)
198{
199 void *p = (void*)ALIGN(start, 16);
200 void *end = (void*)start + size;
Kevin O'Connor4d053eb2012-06-09 13:08:02 -0400201 for (; p<end; p += 16)
202 copy_table(p);
Kevin O'Connordf5a5fd2009-07-28 20:46:03 -0400203}
204
205void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500206coreboot_copy_biostable(void)
Kevin O'Connordf5a5fd2009-07-28 20:46:03 -0400207{
208 struct cb_memory *cbm = CBMemTable;
209 if (! CONFIG_COREBOOT || !cbm)
210 return;
211
212 dprintf(3, "Relocating coreboot bios tables\n");
213
Kevin O'Connordf5a5fd2009-07-28 20:46:03 -0400214 // Scan CB_MEM_TABLE areas for bios tables.
215 int i, count = MEM_RANGE_COUNT(cbm);
216 for (i=0; i<count; i++) {
217 struct cb_memory_range *m = &cbm->map[i];
218 if (m->type == CB_MEM_TABLE)
219 scan_tables(m->start, m->size);
220 }
221
Kevin O'Connor83012de2011-08-28 12:42:15 -0400222 // XXX - create a dummy smbios table for now.
223 if (!SMBiosAddr)
224 smbios_init();
Kevin O'Connordf5a5fd2009-07-28 20:46:03 -0400225}
226
227
228/****************************************************************
Kevin O'Connor592323f2009-04-26 23:17:49 -0400229 * ulzma
230 ****************************************************************/
231
Kevin O'Connor1f836252009-08-16 20:17:35 -0400232// Uncompress data in flash to an area of memory.
Kevin O'Connor592323f2009-04-26 23:17:49 -0400233static int
Kevin O'Connorcbd6ca02009-04-27 21:51:13 -0400234ulzma(u8 *dst, u32 maxlen, const u8 *src, u32 srclen)
Kevin O'Connor592323f2009-04-26 23:17:49 -0400235{
Kevin O'Connor1edc89d2009-04-30 21:50:35 -0400236 dprintf(3, "Uncompressing data %d@%p to %d@%p\n", srclen, src, maxlen, dst);
Kevin O'Connor592323f2009-04-26 23:17:49 -0400237 CLzmaDecoderState state;
238 int ret = LzmaDecodeProperties(&state.Properties, src, LZMA_PROPERTIES_SIZE);
239 if (ret != LZMA_RESULT_OK) {
240 dprintf(1, "LzmaDecodeProperties error - %d\n", ret);
241 return -1;
242 }
243 u8 scratch[15980];
244 int need = (LzmaGetNumProbs(&state.Properties) * sizeof(CProb));
245 if (need > sizeof(scratch)) {
Kevin O'Connor311f8872010-12-26 14:16:22 -0500246 dprintf(1, "LzmaDecode need %d have %d\n", need, (unsigned int)sizeof(scratch));
Kevin O'Connor592323f2009-04-26 23:17:49 -0400247 return -1;
248 }
249 state.Probs = (CProb *)scratch;
250
251 u32 dstlen = *(u32*)(src + LZMA_PROPERTIES_SIZE);
Kevin O'Connorcbd6ca02009-04-27 21:51:13 -0400252 if (dstlen > maxlen) {
253 dprintf(1, "LzmaDecode too large (max %d need %d)\n", maxlen, dstlen);
254 return -1;
255 }
Kevin O'Connor592323f2009-04-26 23:17:49 -0400256 u32 inProcessed, outProcessed;
Kevin O'Connorcbd6ca02009-04-27 21:51:13 -0400257 ret = LzmaDecode(&state, src + LZMA_PROPERTIES_SIZE + 8, srclen
Kevin O'Connor592323f2009-04-26 23:17:49 -0400258 , &inProcessed, dst, dstlen, &outProcessed);
259 if (ret) {
260 dprintf(1, "LzmaDecode returned %d\n", ret);
261 return -1;
262 }
263 return dstlen;
264}
265
266
267/****************************************************************
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400268 * Coreboot flash format
269 ****************************************************************/
270
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400271#define CBFS_HEADER_MAGIC 0x4F524243
272#define CBFS_HEADPTR_ADDR 0xFFFFFFFc
273#define CBFS_VERSION1 0x31313131
274
275struct cbfs_header {
276 u32 magic;
277 u32 version;
278 u32 romsize;
279 u32 bootblocksize;
280 u32 align;
281 u32 offset;
282 u32 pad[2];
283} PACKED;
284
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400285#define CBFS_FILE_MAGIC 0x455649484352414cLL // LARCHIVE
286
287struct cbfs_file {
288 u64 magic;
289 u32 len;
290 u32 type;
291 u32 checksum;
292 u32 offset;
Kevin O'Connor67823442009-04-13 14:14:51 -0400293 char filename[0];
Kevin O'Connor4c0c85a2009-04-11 23:31:29 -0400294} PACKED;
295
Kevin O'Connor59d6ca52012-05-31 00:20:55 -0400296// Copy a file to memory (uncompressing if necessary)
297static int
298cbfs_copyfile(struct romfile_s *file, void *dst, u32 maxlen)
Kevin O'Connor67823442009-04-13 14:14:51 -0400299{
Kevin O'Connord9c93612010-03-20 11:00:45 -0400300 if (!CONFIG_COREBOOT || !CONFIG_COREBOOT_FLASH)
Kevin O'Connor1f836252009-08-16 20:17:35 -0400301 return -1;
302
Kevin O'Connor59d6ca52012-05-31 00:20:55 -0400303 u32 size = file->rawsize;
304 void *src = file->data;
305 if (file->flags) {
Kevin O'Connor34036962009-12-05 18:51:53 -0500306 // Compressed - copy to temp ram and uncompress it.
Kevin O'Connorb4525a02010-07-27 01:14:11 -0400307 void *temp = malloc_tmphigh(size);
Kevin O'Connor59d6ca52012-05-31 00:20:55 -0400308 if (!temp) {
309 warn_noalloc();
Kevin O'Connor34036962009-12-05 18:51:53 -0500310 return -1;
Kevin O'Connor59d6ca52012-05-31 00:20:55 -0400311 }
Kevin O'Connorb4525a02010-07-27 01:14:11 -0400312 iomemcpy(temp, src, size);
Kevin O'Connor34036962009-12-05 18:51:53 -0500313 int ret = ulzma(dst, maxlen, temp, size);
314 yield();
315 free(temp);
316 return ret;
317 }
Kevin O'Connor1f836252009-08-16 20:17:35 -0400318
319 // Not compressed.
320 dprintf(3, "Copying data %d@%p to %d@%p\n", size, src, maxlen, dst);
321 if (size > maxlen) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500322 warn_noalloc();
Kevin O'Connor1f836252009-08-16 20:17:35 -0400323 return -1;
324 }
Kevin O'Connor34036962009-12-05 18:51:53 -0500325 iomemcpy(dst, src, size);
Kevin O'Connor1f836252009-08-16 20:17:35 -0400326 return size;
Kevin O'Connor67823442009-04-13 14:14:51 -0400327}
328
Kevin O'Connor59d6ca52012-05-31 00:20:55 -0400329void
330coreboot_cbfs_setup(void)
331{
332 if (!CONFIG_COREBOOT || !CONFIG_COREBOOT_FLASH)
333 return;
334
335 struct cbfs_header *hdr = *(void **)CBFS_HEADPTR_ADDR;
336 if (hdr->magic != htonl(CBFS_HEADER_MAGIC)) {
337 dprintf(1, "Unable to find CBFS (ptr=%p; got %x not %x)\n"
338 , hdr, hdr->magic, htonl(CBFS_HEADER_MAGIC));
339 return;
340 }
341 dprintf(1, "Found CBFS header at %p\n", hdr);
342
343 struct cbfs_file *cfile = (void *)(0 - ntohl(hdr->romsize)
344 + ntohl(hdr->offset));
345 for (;;) {
346 if (cfile < (struct cbfs_file *)(0xFFFFFFFF - ntohl(hdr->romsize)))
347 break;
348 u64 magic = cfile->magic;
349 if (magic != CBFS_FILE_MAGIC)
350 break;
351 struct romfile_s *file = malloc_tmp(sizeof(*file));
352 if (!file) {
353 warn_noalloc();
354 break;
355 }
356 memset(file, 0, sizeof(*file));
357 strtcpy(file->name, cfile->filename, sizeof(file->name));
358 dprintf(3, "Found CBFS file: %s\n", file->name);
359 file->size = file->rawsize = ntohl(cfile->len);
360 file->id = (u32)cfile;
361 file->copy = cbfs_copyfile;
362 file->data = (void*)cfile + ntohl(cfile->offset);
363 int len = strlen(file->name);
364 if (len > 5 && strcmp(&file->name[len-5], ".lzma") == 0) {
365 // Using compression.
366 file->flags = 1;
367 file->name[len-5] = '\0';
368 file->size = *(u32*)(file->data + LZMA_PROPERTIES_SIZE);
369 }
370 romfile_add(file);
371
372 cfile = (void*)ALIGN((u32)file->data + file->size, ntohl(hdr->align));
373 }
374}
375
Kevin O'Connor67823442009-04-13 14:14:51 -0400376struct cbfs_payload_segment {
377 u32 type;
378 u32 compression;
379 u32 offset;
380 u64 load_addr;
381 u32 len;
382 u32 mem_len;
383} PACKED;
384
385#define PAYLOAD_SEGMENT_BSS 0x20535342
386#define PAYLOAD_SEGMENT_ENTRY 0x52544E45
387
388#define CBFS_COMPRESS_NONE 0
Kevin O'Connor592323f2009-04-26 23:17:49 -0400389#define CBFS_COMPRESS_LZMA 1
Kevin O'Connor67823442009-04-13 14:14:51 -0400390
391struct cbfs_payload {
392 struct cbfs_payload_segment segments[1];
393};
394
395void
Kevin O'Connor1f836252009-08-16 20:17:35 -0400396cbfs_run_payload(struct cbfs_file *file)
Kevin O'Connor67823442009-04-13 14:14:51 -0400397{
Kevin O'Connord9c93612010-03-20 11:00:45 -0400398 if (!CONFIG_COREBOOT || !CONFIG_COREBOOT_FLASH || !file)
Kevin O'Connorcbd6ca02009-04-27 21:51:13 -0400399 return;
Kevin O'Connor1f836252009-08-16 20:17:35 -0400400 dprintf(1, "Run %s\n", file->filename);
Kevin O'Connor67823442009-04-13 14:14:51 -0400401 struct cbfs_payload *pay = (void*)file + ntohl(file->offset);
402 struct cbfs_payload_segment *seg = pay->segments;
403 for (;;) {
Kevin O'Connor67823442009-04-13 14:14:51 -0400404 void *src = (void*)pay + ntohl(seg->offset);
405 void *dest = (void*)ntohl((u32)seg->load_addr);
406 u32 src_len = ntohl(seg->len);
407 u32 dest_len = ntohl(seg->mem_len);
408 switch (seg->type) {
409 case PAYLOAD_SEGMENT_BSS:
410 dprintf(3, "BSS segment %d@%p\n", dest_len, dest);
411 memset(dest, 0, dest_len);
412 break;
413 case PAYLOAD_SEGMENT_ENTRY: {
414 dprintf(1, "Calling addr %p\n", dest);
415 void (*func)() = dest;
416 func();
417 return;
418 }
419 default:
420 dprintf(3, "Segment %x %d@%p -> %d@%p\n"
421 , seg->type, src_len, src, dest_len, dest);
Kevin O'Connor592323f2009-04-26 23:17:49 -0400422 if (seg->compression == htonl(CBFS_COMPRESS_NONE)) {
Kevin O'Connorcbd6ca02009-04-27 21:51:13 -0400423 if (src_len > dest_len)
424 src_len = dest_len;
Kevin O'Connor592323f2009-04-26 23:17:49 -0400425 memcpy(dest, src, src_len);
426 } else if (CONFIG_LZMA
427 && seg->compression == htonl(CBFS_COMPRESS_LZMA)) {
Kevin O'Connorcbd6ca02009-04-27 21:51:13 -0400428 int ret = ulzma(dest, dest_len, src, src_len);
Kevin O'Connor592323f2009-04-26 23:17:49 -0400429 if (ret < 0)
430 return;
431 src_len = ret;
432 } else {
433 dprintf(1, "No support for compression type %x\n"
434 , seg->compression);
435 return;
436 }
Kevin O'Connor67823442009-04-13 14:14:51 -0400437 if (dest_len > src_len)
438 memset(dest + src_len, 0, dest_len - src_len);
439 break;
440 }
441 seg++;
442 }
443}
444
Kevin O'Connor72eee3e2010-12-27 19:07:49 -0500445// Register payloads in "img/" directory with boot system.
Kevin O'Connor89a1efd2011-01-08 12:24:39 -0500446void
447cbfs_payload_setup(void)
Kevin O'Connor72eee3e2010-12-27 19:07:49 -0500448{
Kevin O'Connor59d6ca52012-05-31 00:20:55 -0400449 if (!CONFIG_COREBOOT || !CONFIG_COREBOOT_FLASH)
450 return;
451 struct romfile_s *file = NULL;
Kevin O'Connor72eee3e2010-12-27 19:07:49 -0500452 for (;;) {
Kevin O'Connor59d6ca52012-05-31 00:20:55 -0400453 file = romfile_findprefix("img/", file);
Kevin O'Connor72eee3e2010-12-27 19:07:49 -0500454 if (!file)
455 break;
Kevin O'Connor59d6ca52012-05-31 00:20:55 -0400456 const char *filename = file->name;
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500457 char *desc = znprintf(MAXDESCSIZE, "Payload [%s]", &filename[4]);
Kevin O'Connor59d6ca52012-05-31 00:20:55 -0400458 boot_add_cbfs((void*)file->id, desc
459 , bootprio_find_named_rom(filename, 0));
Kevin O'Connor72eee3e2010-12-27 19:07:49 -0500460 }
461}