diff options
Diffstat (limited to 'common/bloblist.c')
-rw-r--r-- | common/bloblist.c | 73 |
1 files changed, 70 insertions, 3 deletions
diff --git a/common/bloblist.c b/common/bloblist.c index eab63e9ca5..1290fff850 100644 --- a/common/bloblist.c +++ b/common/bloblist.c @@ -57,13 +57,22 @@ static struct bloblist_rec *bloblist_first_blob(struct bloblist_hdr *hdr) return (struct bloblist_rec *)((void *)hdr + hdr->hdr_size); } -static struct bloblist_rec *bloblist_next_blob(struct bloblist_hdr *hdr, - struct bloblist_rec *rec) +static ulong bloblist_blob_end_ofs(struct bloblist_hdr *hdr, + struct bloblist_rec *rec) { ulong offset; offset = (void *)rec - (void *)hdr; offset += rec->hdr_size + ALIGN(rec->size, BLOBLIST_ALIGN); + + return offset; +} + +static struct bloblist_rec *bloblist_next_blob(struct bloblist_hdr *hdr, + struct bloblist_rec *rec) +{ + ulong offset = bloblist_blob_end_ofs(hdr, rec); + if (offset >= hdr->alloced) return NULL; return (struct bloblist_rec *)((void *)hdr + offset); @@ -109,7 +118,7 @@ static int bloblist_addrec(uint tag, int size, int align, /* Calculate the new allocated total */ new_alloced = data_start + ALIGN(size, align); - if (new_alloced >= hdr->size) { + if (new_alloced > hdr->size) { log(LOGC_BLOBLIST, LOGL_ERR, "Failed to allocate %x bytes size=%x, need size=%x\n", size, hdr->size, new_alloced); @@ -215,6 +224,64 @@ int bloblist_ensure_size_ret(uint tag, int *sizep, void **blobp) return 0; } +static int bloblist_resize_rec(struct bloblist_hdr *hdr, + struct bloblist_rec *rec, + int new_size) +{ + int expand_by; /* Number of bytes to expand by (-ve to contract) */ + int new_alloced; /* New value for @hdr->alloced */ + ulong next_ofs; /* Offset of the record after @rec */ + + expand_by = ALIGN(new_size - rec->size, BLOBLIST_ALIGN); + new_alloced = ALIGN(hdr->alloced + expand_by, BLOBLIST_ALIGN); + if (new_size < 0) { + log(LOGC_BLOBLIST, LOGL_DEBUG, + "Attempt to shrink blob size below 0 (%x)\n", new_size); + return log_msg_ret("size", -EINVAL); + } + if (new_alloced > hdr->size) { + log(LOGC_BLOBLIST, LOGL_ERR, + "Failed to allocate %x bytes size=%x, need size=%x\n", + new_size, hdr->size, new_alloced); + return log_msg_ret("alloc", -ENOSPC); + } + + /* Move the following blobs up or down, if this is not the last */ + next_ofs = bloblist_blob_end_ofs(hdr, rec); + if (next_ofs != hdr->alloced) { + memmove((void *)hdr + next_ofs + expand_by, + (void *)hdr + next_ofs, new_alloced - next_ofs); + } + hdr->alloced = new_alloced; + + /* Zero the new part of the blob */ + if (expand_by > 0) { + memset((void *)rec + rec->hdr_size + rec->size, '\0', + new_size - rec->size); + } + + /* Update the size of this blob */ + rec->size = new_size; + + return 0; +} + +int bloblist_resize(uint tag, int new_size) +{ + struct bloblist_hdr *hdr = gd->bloblist; + struct bloblist_rec *rec; + int ret; + + rec = bloblist_findrec(tag); + if (!rec) + return log_msg_ret("find", -ENOENT); + ret = bloblist_resize_rec(hdr, rec, new_size); + if (ret) + return log_msg_ret("resize", ret); + + return 0; +} + static u32 bloblist_calc_chksum(struct bloblist_hdr *hdr) { struct bloblist_rec *rec; |