Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


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