Antonello Dettori (dev@dettori.io) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/15525
-gerrit
commit a127fa7cbcc7c17fd3173277d92e2fbd6a3973a5 Author: Antonello Dettori dev@dettori.io Date: Fri Jul 1 14:28:17 2016 +0200
lib/selfboot: Replace rdev_mmap_full()
Avoid loading the entire payload into memory, instead read each segment when it is required. If the segment is uncompressed rdev_readat() it into memory, otherwise rdev_mmap() the segment and uncompress it. The purpose of the change is to improve the performance during the payload load phase by avoiding any unnecessary mmap operation where possible.
Change-Id: I27cd4358a1747a65a79995114df23e296d1410fd Signed-off-by: Antonello Dettori dev@dettori.io --- src/lib/selfboot.c | 153 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 94 insertions(+), 59 deletions(-)
diff --git a/src/lib/selfboot.c b/src/lib/selfboot.c index 2fdf8ce..ab79e3d 100644 --- a/src/lib/selfboot.c +++ b/src/lib/selfboot.c @@ -36,9 +36,8 @@ struct segment { struct segment *next; struct segment *prev; unsigned long s_dstaddr; - unsigned long s_srcaddr; unsigned long s_memsz; - unsigned long s_filesz; + struct region_device src; int compression; };
@@ -58,6 +57,16 @@ static void segment_insert_after(struct segment *seg, struct segment *new) seg->next = new; }
+static size_t segment_file_sz(const struct segment *seg) +{ + return region_device_sz(&seg->src); +} + +static size_t segment_file_offset(const struct segment *seg) +{ + return region_device_offset(&seg->src); +} + /* The problem: * Static executables all want to share the same addresses * in memory because only a few addresses are reliably present on @@ -137,7 +146,7 @@ static int relocate_segment(unsigned long buffer, struct segment *seg) die ("bounce buffer not supported");
start = seg->s_dstaddr; - middle = start + seg->s_filesz; + middle = start + segment_file_sz(seg); end = start + seg->s_memsz;
printk(BIOS_SPEW, "segment: [0x%016lx, 0x%016lx, 0x%016lx)\n", @@ -155,12 +164,16 @@ static int relocate_segment(unsigned long buffer, struct segment *seg) new->s_memsz = len; seg->s_memsz -= len; seg->s_dstaddr += len; - seg->s_srcaddr += len; - if (seg->s_filesz > len) { - new->s_filesz = len; - seg->s_filesz -= len; + if (segment_file_sz(seg) > len) { + if (rdev_chain(&new->src, &new->src, 0, len) < 0) + return 0; + + if (rdev_chain(&seg->src, &seg->src, len, + segment_file_sz(seg) - len) < 0) + return 0; } else { - seg->s_filesz = 0; + if (rdev_chain(&seg->src, &seg->src, len, 0) < 0) + return 0; }
/* Order by stream offset */ @@ -171,7 +184,7 @@ static int relocate_segment(unsigned long buffer, struct segment *seg)
printk(BIOS_SPEW, " early: [0x%016lx, 0x%016lx, 0x%016lx)\n", new->s_dstaddr, - new->s_dstaddr + new->s_filesz, + new->s_dstaddr + segment_file_sz(new), new->s_dstaddr + new->s_memsz);
ret = 1; @@ -188,19 +201,23 @@ static int relocate_segment(unsigned long buffer, struct segment *seg) seg->s_memsz = len; new->s_memsz -= len; new->s_dstaddr += len; - new->s_srcaddr += len; - if (seg->s_filesz > len) { - seg->s_filesz = len; - new->s_filesz -= len; + if (segment_file_sz(seg) > len) { + if (rdev_chain(&seg->src, &seg->src, 0, len) < 0) + return 0; + + if (rdev_chain(&new->src, &new->src, len, + segment_file_sz(new) - len)) + return 0; } else { - new->s_filesz = 0; + if (rdev_chain(&new->src, &new->src, len, 0) < 0) + return 0; } /* Order by stream offset */ segment_insert_after(seg, new);
printk(BIOS_SPEW, " late: [0x%016lx, 0x%016lx, 0x%016lx)\n", new->s_dstaddr, - new->s_dstaddr + new->s_filesz, + new->s_dstaddr + segment_file_sz(new), new->s_dstaddr + new->s_memsz); } } @@ -214,7 +231,7 @@ static int relocate_segment(unsigned long buffer, struct segment *seg)
printk(BIOS_SPEW, " bounce: [0x%016lx, 0x%016lx, 0x%016lx)\n", seg->s_dstaddr, - seg->s_dstaddr + seg->s_filesz, + seg->s_dstaddr + segment_file_sz(seg), seg->s_dstaddr + seg->s_memsz);
return ret; @@ -234,24 +251,29 @@ static void cbfs_decode_payload_segment(struct cbfs_payload_segment *segment, segment->mem_len = read_be32(&src->mem_len); }
-static int build_self_segment_list( - struct segment *head, - struct cbfs_payload *cbfs_payload, uintptr_t *entry) +static int build_self_segment_list(struct segment *head, + struct region_device *cbfs_payload, uintptr_t *entry) { struct segment *new; - struct cbfs_payload_segment *current_segment, *first_segment, segment; + struct cbfs_payload_segment segment; + size_t offset = 0;
memset(head, 0, sizeof(*head)); head->next = head->prev = head;
- first_segment = &cbfs_payload->segments; + while (true) { + ssize_t ret = rdev_readat(cbfs_payload, &segment, offset, + sizeof(struct cbfs_payload_segment)); + + if (ret != sizeof(struct cbfs_payload_segment)) + return 0;
- for (current_segment = first_segment;; ++current_segment) { printk(BIOS_DEBUG, + "Loading segment from ROM address 0x%p\n", - current_segment); + &segment);
- cbfs_decode_payload_segment(&segment, current_segment); + cbfs_decode_payload_segment(&segment, &segment);
switch (segment.type) { case PAYLOAD_SEGMENT_PARAMS: @@ -268,20 +290,22 @@ static int build_self_segment_list( new->s_dstaddr = segment.load_addr; new->s_memsz = segment.mem_len; new->compression = segment.compression; - new->s_srcaddr = (uintptr_t) - ((unsigned char *)first_segment) - + segment.offset; - new->s_filesz = segment.len; + if (rdev_chain(&new->src, cbfs_payload, segment.offset, + segment.len) < 0) + return 0;
- printk(BIOS_DEBUG, " New segment dstaddr 0x%lx memsize 0x%lx srcaddr 0x%lx filesize 0x%lx\n", - new->s_dstaddr, new->s_memsz, new->s_srcaddr, new->s_filesz); + printk(BIOS_DEBUG, " New segment dstaddr 0x%lx memsize 0x%lx offset 0x%zx filesize 0x%zx\n", + new->s_dstaddr, new->s_memsz, + segment_file_offset(new), segment_file_sz(new));
/* Clean up the values */ - if (new->s_filesz > new->s_memsz) { - new->s_filesz = new->s_memsz; + if (segment_file_sz(new) > new->s_memsz) { + if (rdev_chain(&new->src, &new->src, 0, new->s_memsz) < 0) + return 0; + printk(BIOS_DEBUG, - " cleaned up filesize 0x%lx\n", - new->s_filesz); + " cleaned up filesize 0x%zx\n", + segment_file_sz(new)); } break;
@@ -290,13 +314,11 @@ static int build_self_segment_list( (intptr_t)segment.load_addr, segment.mem_len);
new = malloc(sizeof(*new)); - new->s_filesz = 0; - new->s_srcaddr = (uintptr_t) - ((unsigned char *)first_segment) - + segment.offset; new->s_dstaddr = segment.load_addr; new->s_memsz = segment.mem_len; new->compression = CBFS_COMPRESS_NONE; + if (rdev_chain(&new->src, cbfs_payload, segment.offset, 0) < 0) + return 0; break;
case PAYLOAD_SEGMENT_ENTRY: @@ -323,14 +345,14 @@ static int build_self_segment_list( /* We have found another CODE, DATA or BSS segment */ /* Insert new segment at the end of the list */ segment_insert_before(head, new); + + offset += sizeof(struct cbfs_payload_segment); }
return 1; }
-static int load_self_segments( - struct segment *head, - struct prog *payload) +static int load_self_segments(struct segment *head) { struct segment *ptr; const unsigned long one_meg = (1UL << 20); @@ -378,10 +400,10 @@ static int load_self_segments( }
for(ptr = head->next; ptr != head; ptr = ptr->next) { - unsigned char *dest, *src, *middle, *end; - size_t len, memsz; - printk(BIOS_DEBUG, "Loading Segment: addr: 0x%016lx memsz: 0x%016lx filesz: 0x%016lx\n", - ptr->s_dstaddr, ptr->s_memsz, ptr->s_filesz); + unsigned char *dest, *src; + + printk(BIOS_DEBUG, "Loading Segment: addr: 0x%016lx memsz: 0x%016lx filesz: 0x%016zx\n", + ptr->s_dstaddr, ptr->s_memsz, segment_file_sz(ptr));
/* Modify the segment to load onto the bounce_buffer if necessary. */ @@ -390,21 +412,26 @@ static int load_self_segments( continue; }
- printk(BIOS_DEBUG, "Post relocation: addr: 0x%016lx memsz: 0x%016lx filesz: 0x%016lx\n", - ptr->s_dstaddr, ptr->s_memsz, ptr->s_filesz); + printk(BIOS_DEBUG, "Post relocation: addr: 0x%016lx memsz: 0x%016lx filesz: 0x%016zx\n", + ptr->s_dstaddr, ptr->s_memsz, segment_file_sz(ptr));
/* Compute the boundaries of the segment */ dest = (unsigned char *)(ptr->s_dstaddr); - src = (unsigned char *)(ptr->s_srcaddr); - len = ptr->s_filesz; - memsz = ptr->s_memsz; - end = dest + memsz; + src = NULL;
/* Copy data from the initial buffer */ + unsigned char *middle, *end; + size_t len = segment_file_sz(ptr); + size_t memsz = ptr->s_memsz; + end = dest + memsz; switch(ptr->compression) { case CBFS_COMPRESS_LZMA: { printk(BIOS_DEBUG, "using LZMA\n"); timestamp_add_now(TS_START_ULZMA); + src = rdev_mmap_full(&ptr->src); + if (src == NULL) + return 0; + len = ulzman(src, len, dest, memsz); timestamp_add_now(TS_END_ULZMA); if (!len) /* Decompression Error. */ @@ -414,6 +441,10 @@ static int load_self_segments( case CBFS_COMPRESS_LZ4: { printk(BIOS_DEBUG, "using LZ4\n"); timestamp_add_now(TS_START_ULZ4F); + src = rdev_mmap_full(&ptr->src); + if (src == NULL) + return 0; + len = ulz4fn(src, len, dest, memsz); timestamp_add_now(TS_END_ULZ4F); if (!len) /* Decompression Error. */ @@ -422,13 +453,15 @@ static int load_self_segments( } case CBFS_COMPRESS_NONE: { printk(BIOS_DEBUG, "it's not compressed!\n"); - memcpy(dest, src, len); + if (rdev_readat(&ptr->src, dest, 0, len) < 0) + return 0; break; } default: printk(BIOS_INFO, "CBFS: Unknown compression type %d\n", ptr->compression); return -1; } + /* Calculate middle after any changes to len. */ middle = dest + len; printk(BIOS_SPEW, "[ 0x%08lx, %08lx, 0x%08lx) <- %08lx\n", @@ -471,6 +504,11 @@ static int load_self_segments( */ prog_segment_loaded((uintptr_t)dest, ptr->s_memsz, ptr->next == head ? SEG_FINAL : 0); + + /* munmap the segment that was compressed */ + if (ptr->compression != CBFS_COMPRESS_NONE) + if (rdev_munmap(&ptr->src, src) < 0) + return 0; }
return 1; @@ -480,31 +518,28 @@ void *selfload(struct prog *payload) { uintptr_t entry = 0; struct segment head; - void *data; + struct region_device *data;
- data = rdev_mmap_full(prog_rdev(payload)); + data = prog_rdev(payload);
if (data == NULL) - return NULL; + goto out;
/* Preprocess the self segments */ if (!build_self_segment_list(&head, data, &entry)) goto out;
/* Load the segments */ - if (!load_self_segments(&head, payload)) + if (!load_self_segments(&head)) goto out;
printk(BIOS_SPEW, "Loaded segments\n");
- rdev_munmap(prog_rdev(payload), data); - /* Update the payload's area with the bounce buffer information. */ prog_set_area(payload, (void *)(uintptr_t)bounce_buffer, bounce_size);
return (void *)entry;
out: - rdev_munmap(prog_rdev(payload), data); return NULL; }