1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright 2018 Google, Inc 4 * Written by Simon Glass <sjg@chromium.org> 5 */ 6 7 #include <common.h> 8 #include <bloblist.h> 9 #include <log.h> 10 #include <mapmem.h> 11 #include <spl.h> 12 13 DECLARE_GLOBAL_DATA_PTR; 14 15 struct bloblist_rec *bloblist_first_blob(struct bloblist_hdr *hdr) 16 { 17 if (hdr->alloced <= hdr->hdr_size) 18 return NULL; 19 return (struct bloblist_rec *)((void *)hdr + hdr->hdr_size); 20 } 21 22 struct bloblist_rec *bloblist_next_blob(struct bloblist_hdr *hdr, 23 struct bloblist_rec *rec) 24 { 25 ulong offset; 26 27 offset = (void *)rec - (void *)hdr; 28 offset += rec->hdr_size + ALIGN(rec->size, BLOBLIST_ALIGN); 29 if (offset >= hdr->alloced) 30 return NULL; 31 return (struct bloblist_rec *)((void *)hdr + offset); 32 } 33 34 #define foreach_rec(_rec, _hdr) \ 35 for (_rec = bloblist_first_blob(_hdr); \ 36 _rec; \ 37 _rec = bloblist_next_blob(_hdr, _rec)) 38 39 static struct bloblist_rec *bloblist_findrec(uint tag) 40 { 41 struct bloblist_hdr *hdr = gd->bloblist; 42 struct bloblist_rec *rec; 43 44 if (!hdr) 45 return NULL; 46 47 foreach_rec(rec, hdr) { 48 if (rec->tag == tag) 49 return rec; 50 } 51 52 return NULL; 53 } 54 55 static int bloblist_addrec(uint tag, int size, struct bloblist_rec **recp) 56 { 57 struct bloblist_hdr *hdr = gd->bloblist; 58 struct bloblist_rec *rec; 59 int new_alloced; 60 61 new_alloced = hdr->alloced + sizeof(*rec) + 62 ALIGN(size, BLOBLIST_ALIGN); 63 if (new_alloced >= hdr->size) { 64 log(LOGC_BLOBLIST, LOGL_ERR, 65 "Failed to allocate %x bytes size=%x, need size>=%x\n", 66 size, hdr->size, new_alloced); 67 return log_msg_ret("bloblist add", -ENOSPC); 68 } 69 rec = (void *)hdr + hdr->alloced; 70 hdr->alloced = new_alloced; 71 72 rec->tag = tag; 73 rec->hdr_size = sizeof(*rec); 74 rec->size = size; 75 rec->spare = 0; 76 *recp = rec; 77 78 return 0; 79 } 80 81 static int bloblist_ensurerec(uint tag, struct bloblist_rec **recp, int size) 82 { 83 struct bloblist_rec *rec; 84 85 rec = bloblist_findrec(tag); 86 if (rec) { 87 if (size && size != rec->size) 88 return -ESPIPE; 89 } else { 90 int ret; 91 92 ret = bloblist_addrec(tag, size, &rec); 93 if (ret) 94 return ret; 95 } 96 *recp = rec; 97 98 return 0; 99 } 100 101 void *bloblist_find(uint tag, int size) 102 { 103 struct bloblist_rec *rec; 104 105 rec = bloblist_findrec(tag); 106 if (!rec) 107 return NULL; 108 if (size && size != rec->size) 109 return NULL; 110 111 return (void *)rec + rec->hdr_size; 112 } 113 114 void *bloblist_add(uint tag, int size) 115 { 116 struct bloblist_rec *rec; 117 118 if (bloblist_addrec(tag, size, &rec)) 119 return NULL; 120 121 return rec + 1; 122 } 123 124 int bloblist_ensure_size(uint tag, int size, void **blobp) 125 { 126 struct bloblist_rec *rec; 127 int ret; 128 129 ret = bloblist_ensurerec(tag, &rec, size); 130 if (ret) 131 return ret; 132 *blobp = (void *)rec + rec->hdr_size; 133 134 return 0; 135 } 136 137 void *bloblist_ensure(uint tag, int size) 138 { 139 struct bloblist_rec *rec; 140 141 if (bloblist_ensurerec(tag, &rec, size)) 142 return NULL; 143 144 return (void *)rec + rec->hdr_size; 145 } 146 147 static u32 bloblist_calc_chksum(struct bloblist_hdr *hdr) 148 { 149 struct bloblist_rec *rec; 150 u32 chksum; 151 152 chksum = crc32(0, (unsigned char *)hdr, 153 offsetof(struct bloblist_hdr, chksum)); 154 foreach_rec(rec, hdr) { 155 chksum = crc32(chksum, (void *)rec, rec->hdr_size); 156 chksum = crc32(chksum, (void *)rec + rec->hdr_size, rec->size); 157 } 158 159 return chksum; 160 } 161 162 int bloblist_new(ulong addr, uint size, uint flags) 163 { 164 struct bloblist_hdr *hdr; 165 166 if (size < sizeof(*hdr)) 167 return log_ret(-ENOSPC); 168 if (addr & (BLOBLIST_ALIGN - 1)) 169 return log_ret(-EFAULT); 170 hdr = map_sysmem(addr, size); 171 memset(hdr, '\0', sizeof(*hdr)); 172 hdr->version = BLOBLIST_VERSION; 173 hdr->hdr_size = sizeof(*hdr); 174 hdr->flags = flags; 175 hdr->magic = BLOBLIST_MAGIC; 176 hdr->size = size; 177 hdr->alloced = hdr->hdr_size; 178 hdr->chksum = 0; 179 gd->bloblist = hdr; 180 181 return 0; 182 } 183 184 int bloblist_check(ulong addr, uint size) 185 { 186 struct bloblist_hdr *hdr; 187 u32 chksum; 188 189 hdr = map_sysmem(addr, sizeof(*hdr)); 190 if (hdr->magic != BLOBLIST_MAGIC) 191 return log_msg_ret("Bad magic", -ENOENT); 192 if (hdr->version != BLOBLIST_VERSION) 193 return log_msg_ret("Bad version", -EPROTONOSUPPORT); 194 if (size && hdr->size != size) 195 return log_msg_ret("Bad size", -EFBIG); 196 chksum = bloblist_calc_chksum(hdr); 197 if (hdr->chksum != chksum) { 198 log(LOGC_BLOBLIST, LOGL_ERR, "Checksum %x != %x\n", hdr->chksum, 199 chksum); 200 return log_msg_ret("Bad checksum", -EIO); 201 } 202 gd->bloblist = hdr; 203 204 return 0; 205 } 206 207 int bloblist_finish(void) 208 { 209 struct bloblist_hdr *hdr = gd->bloblist; 210 211 hdr->chksum = bloblist_calc_chksum(hdr); 212 213 return 0; 214 } 215 216 int bloblist_init(void) 217 { 218 bool expected; 219 int ret = -ENOENT; 220 221 /** 222 * Wed expect to find an existing bloblist in the first phase of U-Boot 223 * that runs 224 */ 225 expected = !u_boot_first_phase(); 226 if (expected) 227 ret = bloblist_check(CONFIG_BLOBLIST_ADDR, 228 CONFIG_BLOBLIST_SIZE); 229 if (ret) { 230 log(LOGC_BLOBLIST, expected ? LOGL_WARNING : LOGL_DEBUG, 231 "Existing bloblist not found: creating new bloblist\n"); 232 ret = bloblist_new(CONFIG_BLOBLIST_ADDR, CONFIG_BLOBLIST_SIZE, 233 0); 234 } else { 235 log(LOGC_BLOBLIST, LOGL_DEBUG, "Found existing bloblist\n"); 236 } 237 238 return ret; 239 } 240