coreboot-v2/src/boot/selfboot.c
<<
>>
Prefs
   1/*
   2 * This file is part of the coreboot project.
   3 *
   4 * Copyright (C) 2003 Eric W. Biederman <ebiederm@xmission.com>
   5 * Copyright (C) 2009 Ron Minnich <rminnich@gmail.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; version 2 of the License.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * along with this program; if not, write to the Free Software
  18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
  19 */
  20
  21#include <console/console.h>
  22#include <part/fallback_boot.h>
  23#include <boot/elf.h>
  24#include <boot/elf_boot.h>
  25#include <boot/coreboot_tables.h>
  26#include <ip_checksum.h>
  27#include <stdint.h>
  28#include <stdlib.h>
  29#include <string.h>
  30#include <cbfs.h>
  31#include <lib.h>
  32
  33#ifndef CONFIG_BIG_ENDIAN
  34#define ntohl(x) ( ((x&0xff)<<24) | ((x&0xff00)<<8) | \
  35                ((x&0xff0000) >> 8) | ((x&0xff000000) >> 24) )
  36#else
  37#define ntohl(x) (x)
  38#endif
  39
  40/* Maximum physical address we can use for the coreboot bounce buffer.
  41 */
  42#ifndef MAX_ADDR
  43#define MAX_ADDR -1UL
  44#endif
  45
  46extern unsigned char _ram_seg;
  47extern unsigned char _eram_seg;
  48
  49struct segment {
  50        struct segment *next;
  51        struct segment *prev;
  52        struct segment *phdr_next;
  53        struct segment *phdr_prev;
  54        unsigned long s_dstaddr;
  55        unsigned long s_srcaddr;
  56        unsigned long s_memsz;
  57        unsigned long s_filesz;
  58        int compression;
  59};
  60
  61struct verify_callback {
  62        struct verify_callback *next;
  63        int (*callback)(struct verify_callback *vcb, 
  64                Elf_ehdr *ehdr, Elf_phdr *phdr, struct segment *head);
  65        unsigned long desc_offset;
  66        unsigned long desc_addr;
  67};
  68
  69struct ip_checksum_vcb {
  70        struct verify_callback data;
  71        unsigned short ip_checksum;
  72};
  73
  74static int selfboot(struct lb_memory *mem, struct cbfs_payload *payload);
  75
  76void * cbfs_load_payload(struct lb_memory *lb_mem, const char *name)
  77{
  78        struct cbfs_payload *payload;
  79
  80        payload = (struct cbfs_payload *)cbfs_find_file(name, CBFS_TYPE_PAYLOAD);
  81        if (payload == NULL)
  82                return (void *) -1;
  83        printk_debug("Got a payload\n");
  84
  85        selfboot(lb_mem, payload);
  86        printk_emerg("SELFBOOT RETURNED!\n");
  87
  88        return (void *) -1;
  89}
  90
  91/* The problem:  
  92 * Static executables all want to share the same addresses
  93 * in memory because only a few addresses are reliably present on
  94 * a machine, and implementing general relocation is hard.
  95 *
  96 * The solution:
  97 * - Allocate a buffer the size of the coreboot image plus additional
  98 *   required space.
  99 * - Anything that would overwrite coreboot copy into the lower part of
 100 *   the buffer. 
 101 * - After loading an ELF image copy coreboot to the top of the buffer.
 102 * - Then jump to the loaded image.
 103 * 
 104 * Benefits:
 105 * - Nearly arbitrary standalone executables can be loaded.
 106 * - Coreboot is preserved, so it can be returned to.
 107 * - The implementation is still relatively simple,
 108 *   and much simpler then the general case implemented in kexec.
 109 * 
 110 */
 111
 112static unsigned long bounce_size, bounce_buffer;
 113
 114static void get_bounce_buffer(struct lb_memory *mem, unsigned long req_size)
 115{
 116        unsigned long lb_size;
 117        unsigned long mem_entries;
 118        unsigned long buffer;
 119        int i;
 120        lb_size = (unsigned long)(&_eram_seg - &_ram_seg);
 121        /* Double coreboot size so I have somewhere to place a copy to return to */
 122        lb_size = req_size + lb_size;
 123        mem_entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
 124        buffer = 0;
 125        for(i = 0; i < mem_entries; i++) {
 126                unsigned long mstart, mend;
 127                unsigned long msize;
 128                unsigned long tbuffer;
 129                if (mem->map[i].type != LB_MEM_RAM)
 130                        continue;
 131                if (unpack_lb64(mem->map[i].start) > MAX_ADDR)
 132                        continue;
 133                if (unpack_lb64(mem->map[i].size) < lb_size)
 134                        continue;
 135                mstart = unpack_lb64(mem->map[i].start);
 136                msize = MAX_ADDR - mstart +1;
 137                if (msize > unpack_lb64(mem->map[i].size))
 138                        msize = unpack_lb64(mem->map[i].size);
 139                mend = mstart + msize;
 140                tbuffer = mend - lb_size;
 141                if (tbuffer < buffer) 
 142                        continue;
 143                buffer = tbuffer;
 144        }
 145        bounce_buffer = buffer;
 146        bounce_size = req_size;
 147}
 148
 149static int valid_area(struct lb_memory *mem, unsigned long buffer,
 150        unsigned long start, unsigned long len)
 151{
 152        /* Check through all of the memory segments and ensure
 153         * the segment that was passed in is completely contained
 154         * in RAM.
 155         */
 156        int i;
 157        unsigned long end = start + len;
 158        unsigned long mem_entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
 159
 160        /* See if I conflict with the bounce buffer */
 161        if (end >= buffer) {
 162                return 0;
 163        }
 164
 165        /* Walk through the table of valid memory ranges and see if I
 166         * have a match.
 167         */
 168        for(i = 0; i < mem_entries; i++) {
 169                uint64_t mstart, mend;
 170                uint32_t mtype;
 171                mtype = mem->map[i].type;
 172                mstart = unpack_lb64(mem->map[i].start);
 173                mend = mstart + unpack_lb64(mem->map[i].size);
 174                if ((mtype == LB_MEM_RAM) && (start < mend) && (end > mstart)) {
 175                        break;
 176                }
 177                if ((mtype == LB_MEM_TABLE) && (start < mend) && (end > mstart)) {
 178                        printk_err("Payload is overwriting Coreboot tables.\n");
 179                        break;
 180                }
 181        }
 182        if (i == mem_entries) {
 183                printk_err("No matching ram area found for range:\n");
 184                printk_err("  [0x%016lx, 0x%016lx)\n", start, end);
 185                printk_err("Ram areas\n");
 186                for(i = 0; i < mem_entries; i++) {
 187                        uint64_t mstart, mend;
 188                        uint32_t mtype;
 189                        mtype = mem->map[i].type;
 190                        mstart = unpack_lb64(mem->map[i].start);
 191                        mend = mstart + unpack_lb64(mem->map[i].size);
 192                        printk_err("  [0x%016lx, 0x%016lx) %s\n",
 193                                (unsigned long)mstart, 
 194                                (unsigned long)mend, 
 195                                (mtype == LB_MEM_RAM)?"RAM":"Reserved");
 196                        
 197                }
 198                return 0;
 199        }
 200        return 1;
 201}
 202
 203static const unsigned long lb_start = (unsigned long)&_ram_seg;
 204static const unsigned long lb_end = (unsigned long)&_eram_seg;
 205
 206static int overlaps_coreboot(struct segment *seg)
 207{
 208        unsigned long start, end;
 209        start = seg->s_dstaddr;
 210        end = start + seg->s_memsz;
 211        return !((end <= lb_start) || (start >= lb_end));
 212}
 213
 214static void relocate_segment(unsigned long buffer, struct segment *seg)
 215{
 216        /* Modify all segments that want to load onto coreboot
 217         * to load onto the bounce buffer instead.
 218         */
 219        unsigned long start, middle, end;
 220
 221        printk_spew("lb: [0x%016lx, 0x%016lx)\n", 
 222                lb_start, lb_end);
 223
 224        /* I don't conflict with coreboot so get out of here */
 225        if (!overlaps_coreboot(seg))
 226                return;
 227
 228        start = seg->s_dstaddr;
 229        middle = start + seg->s_filesz;
 230        end = start + seg->s_memsz;
 231
 232        printk_spew("segment: [0x%016lx, 0x%016lx, 0x%016lx)\n", 
 233                start, middle, end);
 234
 235        if (seg->compression == CBFS_COMPRESS_NONE) {
 236                /* Slice off a piece at the beginning
 237                 * that doesn't conflict with coreboot.
 238                 */
 239                if (start < lb_start) {
 240                        struct segment *new;
 241                        unsigned long len = lb_start - start;
 242                        new = malloc(sizeof(*new));
 243                        *new = *seg;
 244                        new->s_memsz = len;
 245                        seg->s_memsz -= len;
 246                        seg->s_dstaddr += len;
 247                        seg->s_srcaddr += len;
 248                        if (seg->s_filesz > len) {
 249                                new->s_filesz = len;
 250                                seg->s_filesz -= len;
 251                        } else {
 252                                seg->s_filesz = 0;
 253                        }
 254
 255                        /* Order by stream offset */
 256                        new->next = seg;
 257                        new->prev = seg->prev;
 258                        seg->prev->next = new;
 259                        seg->prev = new;
 260                        /* Order by original program header order */
 261                        new->phdr_next = seg;
 262                        new->phdr_prev = seg->phdr_prev;
 263                        seg->phdr_prev->phdr_next = new;
 264                        seg->phdr_prev = new;
 265
 266                        /* compute the new value of start */
 267                        start = seg->s_dstaddr;
 268                        
 269                        printk_spew("   early: [0x%016lx, 0x%016lx, 0x%016lx)\n", 
 270                                new->s_dstaddr, 
 271                                new->s_dstaddr + new->s_filesz,
 272                                new->s_dstaddr + new->s_memsz);
 273                }
 274                        
 275                /* Slice off a piece at the end 
 276                 * that doesn't conflict with coreboot 
 277                 */
 278                if (end > lb_end) {
 279                        unsigned long len = lb_end - start;
 280                        struct segment *new;
 281                        new = malloc(sizeof(*new));
 282                        *new = *seg;
 283                        seg->s_memsz = len;
 284                        new->s_memsz -= len;
 285                        new->s_dstaddr += len;
 286                        new->s_srcaddr += len;
 287                        if (seg->s_filesz > len) {
 288                                seg->s_filesz = len;
 289                                new->s_filesz -= len;
 290                        } else {
 291                                new->s_filesz = 0;
 292                        }
 293                        /* Order by stream offset */
 294                        new->next = seg->next;
 295                        new->prev = seg;
 296                        seg->next->prev = new;
 297                        seg->next = new;
 298                        /* Order by original program header order */
 299                        new->phdr_next = seg->phdr_next;
 300                        new->phdr_prev = seg;
 301                        seg->phdr_next->phdr_prev = new;
 302                        seg->phdr_next = new;
 303
 304                        printk_spew("   late: [0x%016lx, 0x%016lx, 0x%016lx)\n", 
 305                                new->s_dstaddr, 
 306                                new->s_dstaddr + new->s_filesz,
 307                                new->s_dstaddr + new->s_memsz);
 308                }
 309        }
 310
 311        /* Now retarget this segment onto the bounce buffer */
 312        /* sort of explanation: the buffer is a 1:1 mapping to coreboot. 
 313         * so you will make the dstaddr be this buffer, and it will get copied
 314         * later to where coreboot lives.
 315         */
 316        seg->s_dstaddr = buffer + (seg->s_dstaddr - lb_start);
 317
 318        printk_spew(" bounce: [0x%016lx, 0x%016lx, 0x%016lx)\n", 
 319                seg->s_dstaddr, 
 320                seg->s_dstaddr + seg->s_filesz, 
 321                seg->s_dstaddr + seg->s_memsz);
 322}
 323
 324
 325static int build_self_segment_list(
 326        struct segment *head, 
 327        struct lb_memory *mem,
 328        struct cbfs_payload *payload, u32 *entry)
 329{
 330        struct segment *new;
 331        struct segment *ptr;
 332        struct cbfs_payload_segment *segment, *first_segment;
 333        memset(head, 0, sizeof(*head));
 334        head->phdr_next = head->phdr_prev = head;
 335        head->next = head->prev = head;
 336        first_segment = segment = &payload->segments;
 337
 338        while(1) {
 339                printk_debug("Loading segment from rom address 0x%p\n", segment);
 340                switch(segment->type) {
 341                case PAYLOAD_SEGMENT_PARAMS:
 342                        printk_debug("  parameter section (skipped)\n");
 343                        segment++;
 344                        continue;
 345
 346                case PAYLOAD_SEGMENT_CODE:
 347                case PAYLOAD_SEGMENT_DATA:
 348                        printk_debug("  %s (compression=%x)\n", 
 349                                        segment->type == PAYLOAD_SEGMENT_CODE ?  "code" : "data",
 350                                        ntohl(segment->compression));
 351                        new = malloc(sizeof(*new));
 352                        new->s_dstaddr = ntohl((u32) segment->load_addr);
 353                        new->s_memsz = ntohl(segment->mem_len);
 354                        new->compression = ntohl(segment->compression);
 355
 356                        new->s_srcaddr = (u32) ((unsigned char *) first_segment) + ntohl(segment->offset);
 357                        new->s_filesz = ntohl(segment->len);
 358                        printk_debug("  New segment dstaddr 0x%lx memsize 0x%lx srcaddr 0x%lx filesize 0x%lx\n",
 359                                new->s_dstaddr, new->s_memsz, new->s_srcaddr, new->s_filesz);
 360                        /* Clean up the values */
 361                        if (new->s_filesz > new->s_memsz)  {
 362                                new->s_filesz = new->s_memsz;
 363                        }
 364                        printk_debug("  (cleaned up) New segment addr 0x%lx size 0x%lx offset 0x%lx filesize 0x%lx\n",
 365                                new->s_dstaddr, new->s_memsz, new->s_srcaddr, new->s_filesz);
 366                        break;
 367
 368                case PAYLOAD_SEGMENT_BSS:
 369                        printk_debug("  BSS 0x%p (%d byte)\n", (void *) ntohl((u32) segment->load_addr),
 370                                 ntohl(segment->mem_len));
 371                        new = malloc(sizeof(*new));
 372                        new->s_filesz = 0;
 373                        new->s_dstaddr = ntohl((u32) segment->load_addr);
 374                        new->s_memsz = ntohl(segment->mem_len);
 375                        break;
 376
 377                case PAYLOAD_SEGMENT_ENTRY:
 378                        printk_debug("  Entry Point 0x%p\n", (void *) ntohl((u32) segment->load_addr));
 379                        *entry =  ntohl((u32) segment->load_addr);
 380                        /* Per definition, a payload always has the entry point
 381                         * as last segment. Thus, we use the occurence of the
 382                         * entry point as break condition for the loop.
 383                         * Can we actually just look at the number of section?
 384                         */
 385                        return 1;
 386
 387                default:
 388                        /* We found something that we don't know about. Throw
 389                         * hands into the sky and run away!
 390                         */
 391                        printk_emerg("Bad segment type %x\n", segment->type);
 392                        return -1;
 393                }
 394
 395                segment++;
 396
 397                // FIXME: Explain what this is 
 398                for(ptr = head->next; ptr != head; ptr = ptr->next) {
 399                        if (new->s_srcaddr < ntohl((u32) segment->load_addr))
 400                                break;
 401                }
 402
 403                /* Order by stream offset */
 404                new->next = ptr;
 405                new->prev = ptr->prev;
 406                ptr->prev->next = new;
 407                ptr->prev = new;
 408
 409                /* Order by original program header order */
 410                new->phdr_next = head;
 411                new->phdr_prev = head->phdr_prev;
 412                head->phdr_prev->phdr_next  = new;
 413                head->phdr_prev = new;
 414        }
 415
 416        return 1;
 417}
 418
 419static int load_self_segments(
 420        struct segment *head,
 421        struct lb_memory *mem,
 422        struct cbfs_payload *payload)
 423{
 424        struct segment *ptr;
 425        
 426        unsigned long bounce_high = lb_end;
 427        for(ptr = head->next; ptr != head; ptr = ptr->next) {
 428                if (!overlaps_coreboot(ptr)) continue;
 429                if (ptr->s_dstaddr + ptr->s_memsz > bounce_high)
 430                        bounce_high = ptr->s_dstaddr + ptr->s_memsz;
 431        }
 432        get_bounce_buffer(mem, bounce_high - lb_start);
 433        if (!bounce_buffer) {
 434                printk_err("Could not find a bounce buffer...\n");
 435                return 0;
 436        }
 437        for(ptr = head->next; ptr != head; ptr = ptr->next) {
 438                /* Verify the memory addresses in the segment are valid */
 439                if (!valid_area(mem, bounce_buffer, ptr->s_dstaddr, ptr->s_memsz))
 440                        return 0;
 441        }
 442        for(ptr = head->next; ptr != head; ptr = ptr->next) {
 443                unsigned char *dest, *src;
 444                printk_debug("Loading Segment: addr: 0x%016lx memsz: 0x%016lx filesz: 0x%016lx\n",
 445                        ptr->s_dstaddr, ptr->s_memsz, ptr->s_filesz);
 446                
 447                /* Modify the segment to load onto the bounce_buffer if necessary.
 448                 */
 449                relocate_segment(bounce_buffer, ptr);
 450
 451                printk_debug("Post relocation: addr: 0x%016lx memsz: 0x%016lx filesz: 0x%016lx\n",
 452                        ptr->s_dstaddr, ptr->s_memsz, ptr->s_filesz);
 453
 454                /* Compute the boundaries of the segment */
 455                dest = (unsigned char *)(ptr->s_dstaddr);
 456                src = (unsigned char *)(ptr->s_srcaddr);
 457                
 458                /* Copy data from the initial buffer */
 459                if (ptr->s_filesz) {
 460                        unsigned char *middle, *end;
 461                        size_t len;
 462                        len = ptr->s_filesz;
 463                        switch(ptr->compression) {
 464                                case CBFS_COMPRESS_LZMA: {
 465                                        printk_debug("using LZMA\n");
 466                                        len = ulzma(src, dest);
 467                                        break;
 468                                }
 469#if CONFIG_COMPRESSED_PAYLOAD_NRV2B==1
 470                                case CBFS_COMPRESS_NRV2B: {
 471                                        printk_debug("using NRV2B\n");
 472                                        unsigned long unrv2b(u8 *src, u8 *dst, unsigned long *ilen_p);
 473                                        unsigned long tmp;
 474                                        len = unrv2b(src, dest, &tmp);
 475                                        break;
 476                                }
 477#endif
 478                                case CBFS_COMPRESS_NONE: {
 479                                        printk_debug("it's not compressed!\n");
 480                                        memcpy(dest, src, len);
 481                                        break;
 482                                }
 483                                default:
 484                                        printk_info( "CBFS:  Unknown compression type %d\n", ptr->compression);
 485                                        return -1;
 486                        }
 487                        end = dest + ptr->s_memsz;
 488                        middle = dest + len;
 489                        printk_spew("[ 0x%016lx, %016lx, 0x%016lx) <- %016lx\n",
 490                                (unsigned long)dest,
 491                                (unsigned long)middle,
 492                                (unsigned long)end,
 493                                (unsigned long)src);
 494
 495                        /* Zero the extra bytes between middle & end */
 496                        if (middle < end) {
 497                                printk_debug("Clearing Segment: addr: 0x%016lx memsz: 0x%016lx\n",
 498                                        (unsigned long)middle, (unsigned long)(end - middle));
 499                        
 500                                /* Zero the extra bytes */
 501                                memset(middle, 0, end - middle);
 502                        }
 503                        /* Copy the data that's outside the area that shadows coreboot_ram */
 504                        printk_debug("dest %p, end %p, bouncebuffer %lx\n", dest, end, bounce_buffer);
 505                        if ((unsigned long)end > bounce_buffer) {
 506                                if ((unsigned long)dest < bounce_buffer) {
 507                                        unsigned char *from = dest;
 508                                        unsigned char *to = (unsigned char*)(lb_start-(bounce_buffer-(unsigned long)dest));
 509                                        unsigned long amount = bounce_buffer-(unsigned long)dest;
 510                                        printk_debug("move prefix around: from %p, to %p, amount: %lx\n", from, to, amount);
 511                                        memcpy(to, from, amount);
 512                                }
 513                                if ((unsigned long)end > bounce_buffer + (lb_end - lb_start)) {
 514                                        unsigned long from = bounce_buffer + (lb_end - lb_start);
 515                                        unsigned long to = lb_end;
 516                                        unsigned long amount = (unsigned long)end - from;
 517                                        printk_debug("move suffix around: from %lx, to %lx, amount: %lx\n", from, to, amount);
 518                                        memcpy((char*)to, (char*)from, amount);
 519                                }
 520                        }
 521                }
 522        }
 523        return 1;
 524}
 525
 526static int selfboot(struct lb_memory *mem, struct cbfs_payload *payload)
 527{
 528        u32 entry=0;
 529        struct segment head;
 530
 531        /* Preprocess the self segments */
 532        if (!build_self_segment_list(&head, mem, payload, &entry))
 533                goto out;
 534
 535        /* Load the segments */
 536        if (!load_self_segments(&head, mem, payload))
 537                goto out;
 538
 539        printk_spew("Loaded segments\n");
 540
 541        /* Reset to booting from this image as late as possible */
 542        boot_successful();
 543
 544        printk_debug("Jumping to boot code at %x\n", entry);
 545        post_code(0xfe);
 546
 547        /* Jump to kernel */
 548        jmp_to_elf_entry((void*)entry, bounce_buffer, bounce_size);
 549        return 1;
 550
 551 out:
 552        return 0;
 553}
 554
 555
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.