Vladimir Serbinenko (phcoder@gmail.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/13762
-gerrit
commit f7e4c49995300e5aa00da19df2eae3e7847263d1 Author: Vladimir Serbinenko phcoder@gmail.com Date: Sun Feb 21 19:04:57 2016 +0100
Support self-relocatable images.
On x86 we have 0x0-0x90000 and 0x100000-0xf00000 which are more or less guaranteed to be RAM. On ARM we don't have such a range. It's possible to make an image containing only PIC or self-relocatable. Now we need coreboot to choose a load address itself. As an additional advantage payload is loaded as high as possible, simplifying payload code when it needs to load something else.
Change-Id: I27d5825e5b748a2220506005f96eceaef6d94cc2 Signed-off-by: Vladimir Serbinenko phcoder@gmail.com --- Documentation/cbfs.txt | 14 ++++ payloads/libpayload/include/cbfs_core.h | 1 + src/commonlib/include/commonlib/cbfs_serialized.h | 1 + src/lib/selfboot.c | 89 +++++++++++++++++------ util/cbfstool/cbfs-mkpayload.c | 18 +++++ util/cbfstool/cbfs.h | 1 + util/cbfstool/cbfs_image.c | 4 + util/nvramtool/cbfs.h | 1 + 8 files changed, 108 insertions(+), 21 deletions(-)
diff --git a/Documentation/cbfs.txt b/Documentation/cbfs.txt index 7ecc901..918353d 100644 --- a/Documentation/cbfs.txt +++ b/Documentation/cbfs.txt @@ -385,6 +385,8 @@ PAYLOAD_SEGMENT_PARAMS 0x41524150 The segment contains information for the payload PAYLOAD_SEGMENT_ENTRY 0x52544E45 The segment contains the entry point for the payload +PAYLOAD_SEGMENT_FLAGS 0x47414c46 The segment contains information for + for loader
'compression' is the compression scheme for the segment. Each segment can be independently compressed. There are three compression types defined by @@ -405,6 +407,18 @@ component.
The data will located immediately following the last segment.
+Format of FLAGS is following (everything is in native-endian): +compatible_flags uint32 Flags that can be ignored by older + loaders. + self-relocatable bit0 Image is self-relocatable relocatable + and can be loaded shifted by any offset + divisible by align +incompatible_flags uint32 Flags that loader must abort if it sees + an unknown flag. Currently none. +align uint32 alignment requirement for + self-relocatable images + + === Option ROMS ===
The third specified component type will be Option ROMs. Option ROMS will diff --git a/payloads/libpayload/include/cbfs_core.h b/payloads/libpayload/include/cbfs_core.h index 4c59f41..eea63a5 100644 --- a/payloads/libpayload/include/cbfs_core.h +++ b/payloads/libpayload/include/cbfs_core.h @@ -216,6 +216,7 @@ struct cbfs_payload { #define PAYLOAD_SEGMENT_DATA 0x41544144 #define PAYLOAD_SEGMENT_BSS 0x20535342 #define PAYLOAD_SEGMENT_PARAMS 0x41524150 +#define PAYLOAD_SEGMENT_FLAGS 0x47414c46 #define PAYLOAD_SEGMENT_ENTRY 0x52544E45
struct cbfs_optionrom { diff --git a/src/commonlib/include/commonlib/cbfs_serialized.h b/src/commonlib/include/commonlib/cbfs_serialized.h index bea5d6b..9d15319 100644 --- a/src/commonlib/include/commonlib/cbfs_serialized.h +++ b/src/commonlib/include/commonlib/cbfs_serialized.h @@ -178,6 +178,7 @@ struct cbfs_payload { #define PAYLOAD_SEGMENT_DATA 0x41544144 #define PAYLOAD_SEGMENT_BSS 0x20535342 #define PAYLOAD_SEGMENT_PARAMS 0x41524150 +#define PAYLOAD_SEGMENT_FLAGS 0x47414c46 #define PAYLOAD_SEGMENT_ENTRY 0x52544E45
struct cbfs_optionrom { diff --git a/src/lib/selfboot.c b/src/lib/selfboot.c index f3a1e52..3a1afc4 100644 --- a/src/lib/selfboot.c +++ b/src/lib/selfboot.c @@ -210,7 +210,8 @@ static int relocate_segment(unsigned long buffer, struct segment *seg)
static int build_self_segment_list( struct segment *head, - struct cbfs_payload *cbfs_payload, uintptr_t *entry) + struct cbfs_payload *cbfs_payload, uintptr_t *entry, + uint32_t *align) { struct segment *new; struct segment *ptr; @@ -218,6 +219,7 @@ static int build_self_segment_list( memset(head, 0, sizeof(*head)); head->next = head->prev = head; first_segment = segment = &cbfs_payload->segments; + *align = 0;
while(1) { printk(BIOS_DEBUG, "Loading segment from rom address 0x%p\n", segment); @@ -227,6 +229,25 @@ static int build_self_segment_list( segment++; continue;
+ case PAYLOAD_SEGMENT_FLAGS: + printk(BIOS_DEBUG, " flags section\n"); + uint32_t *flags; + flags = (uint32_t *)(((unsigned char *)first_segment) + + ntohl(segment->offset)); + if (flags[1]) + { + printk(BIOS_EMERG, "Unsupported incompatible flags: %x\n", flags[1]); + return -1; + } + if (flags[0] & 1) + { + printk(BIOS_DEBUG, "Relocatable image align %x\n", flags[2]); + *align = flags[2] ? : 1; + } + + segment++; + continue; + case PAYLOAD_SEGMENT_CODE: case PAYLOAD_SEGMENT_DATA: printk(BIOS_DEBUG, " %s (compression=%x)\n", @@ -305,7 +326,9 @@ static int build_self_segment_list(
static int load_self_segments( struct segment *head, - struct prog *payload) + struct prog *payload, + uint32_t align, + uintptr_t *entry) { struct segment *ptr; struct segment *last_non_empty; @@ -318,25 +341,48 @@ static int load_self_segments( if (ptr->s_filesz != 0) last_non_empty = ptr;
- for(ptr = head->next; ptr != head; ptr = ptr->next) { - if (bootmem_region_targets_usable_ram(ptr->s_dstaddr, - ptr->s_memsz)) - continue; - - if (ptr->s_dstaddr < one_meg && - (ptr->s_dstaddr + ptr->s_memsz) <= one_meg) { - printk(BIOS_DEBUG, - "Payload being loaded below 1MiB " - "without region being marked as RAM usable.\n"); - continue; + if (align) { + uintptr_t lowest = ~(uintptr_t)0, highest = 0; + uintptr_t offset = 0; + void *target; + for(ptr = head->next; ptr != head; ptr = ptr->next) { + if (lowest > ptr->s_dstaddr) + lowest = ptr->s_dstaddr; + if (highest < ptr->s_dstaddr + ptr->s_memsz) + highest = ptr->s_dstaddr + ptr->s_memsz; + } + target = bootmem_allocate_buffer(highest - lowest + align - 1); + if (!target) { + printk(BIOS_ERR, "Unable to allocate 0x%lx bytes\n", + (unsigned long) (highest - lowest + align - 1)); + return 0; } + offset = ALIGN_UP ((uintptr_t)target - lowest, align); + for(ptr = head->next; ptr != head; ptr = ptr->next) { + ptr->s_dstaddr += offset; + } + *entry += offset; + } else { + for(ptr = head->next; ptr != head; ptr = ptr->next) { + if (bootmem_region_targets_usable_ram(ptr->s_dstaddr, + ptr->s_memsz)) + continue;
- /* Payload segment not targeting RAM. */ - printk(BIOS_ERR, "SELF Payload doesn't target RAM:\n"); - printk(BIOS_ERR, "Failed Segment: 0x%lx, %lu bytes\n", - ptr->s_dstaddr, ptr->s_memsz); - bootmem_dump_ranges(); - return 0; + if (ptr->s_dstaddr < one_meg && + (ptr->s_dstaddr + ptr->s_memsz) <= one_meg) { + printk(BIOS_DEBUG, + "Payload being loaded below 1MiB " + "without region being marked as RAM usable.\n"); + continue; + } + + /* Payload segment not targeting RAM. */ + printk(BIOS_ERR, "SELF Payload doesn't target RAM:\n"); + printk(BIOS_ERR, "Failed Segment: 0x%lx, %lu bytes\n", + ptr->s_dstaddr, ptr->s_memsz); + bootmem_dump_ranges(); + return 0; + } }
for(ptr = head->next; ptr != head; ptr = ptr->next) { @@ -452,6 +498,7 @@ void *selfload(struct prog *payload) uintptr_t entry = 0; struct segment head; void *data; + uint32_t align;
data = rdev_mmap_full(prog_rdev(payload));
@@ -459,11 +506,11 @@ void *selfload(struct prog *payload) return NULL;
/* Preprocess the self segments */ - if (!build_self_segment_list(&head, data, &entry)) + if (!build_self_segment_list(&head, data, &entry, &align)) goto out;
/* Load the segments */ - if (!load_self_segments(&head, payload)) + if (!load_self_segments(&head, payload, align, &entry)) goto out;
printk(BIOS_SPEW, "Loaded segments\n"); diff --git a/util/cbfstool/cbfs-mkpayload.c b/util/cbfstool/cbfs-mkpayload.c index 45d36f4..846cd74 100644 --- a/util/cbfstool/cbfs-mkpayload.c +++ b/util/cbfstool/cbfs-mkpayload.c @@ -110,6 +110,10 @@ int parse_elf_to_payload(const struct buffer *input, struct buffer *output, segments++; isize += (unsigned int)shdr[i].sh_size; } + if (!strcmp(name, ".coreboot_flags")) { + segments++; + isize += (unsigned int)shdr[i].sh_size; + } }
/* Now, regular headers - we only care about PT_LOAD headers, @@ -179,6 +183,20 @@ int parse_elf_to_payload(const struct buffer *input, struct buffer *output,
segments++; } + if (!strcmp(name, ".coreboot_flags")) { + segs[segments].type = PAYLOAD_SEGMENT_FLAGS; + segs[segments].load_addr = 0; + segs[segments].len = (unsigned int)shdr[i].sh_size; + segs[segments].offset = doffset; + + memcpy((unsigned long *)(output->data + doffset), + &header[shdr[i].sh_offset], shdr[i].sh_size); + + doffset += segs[segments].len; + osize += segs[segments].len; + + segments++; + } }
for (i = 0; i < headers; i++) { diff --git a/util/cbfstool/cbfs.h b/util/cbfstool/cbfs.h index 641c6a1..6e1dd77 100644 --- a/util/cbfstool/cbfs.h +++ b/util/cbfstool/cbfs.h @@ -152,6 +152,7 @@ struct cbfs_stage { #define PAYLOAD_SEGMENT_DATA makemagic('D', 'A', 'T', 'A') #define PAYLOAD_SEGMENT_BSS makemagic('B', 'S', 'S', ' ') #define PAYLOAD_SEGMENT_PARAMS makemagic('P', 'A', 'R', 'A') +#define PAYLOAD_SEGMENT_FLAGS makemagic('F', 'L', 'A', 'G') #define PAYLOAD_SEGMENT_ENTRY makemagic('E', 'N', 'T', 'R')
struct cbfs_payload_segment { diff --git a/util/cbfstool/cbfs_image.c b/util/cbfstool/cbfs_image.c index 95e6f42..9d5441c 100644 --- a/util/cbfstool/cbfs_image.c +++ b/util/cbfstool/cbfs_image.c @@ -1093,6 +1093,10 @@ static int cbfs_print_decoded_payload_segment_info( fprintf(fp, " parameters\n"); break;
+ case PAYLOAD_SEGMENT_FLAGS: + fprintf(fp, " flags\n"); + break; + default: fprintf(fp, " 0x%x (%s compression, offset: 0x%x, " "load: 0x%" PRIx64 ", length: %d/%d\n", diff --git a/util/nvramtool/cbfs.h b/util/nvramtool/cbfs.h index 58ef126..3158a39 100644 --- a/util/nvramtool/cbfs.h +++ b/util/nvramtool/cbfs.h @@ -159,6 +159,7 @@ struct cbfs_payload { #define PAYLOAD_SEGMENT_BSS 0x20535342 #define PAYLOAD_SEGMENT_PARAMS 0x41524150 #define PAYLOAD_SEGMENT_ENTRY 0x52544E45 +#define PAYLOAD_SEGMENT_FLAGS 0x47414c46
struct cbfs_optionrom { u32 compression;