summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--firmware/buflib.c238
1 files changed, 133 insertions, 105 deletions
diff --git a/firmware/buflib.c b/firmware/buflib.c
index e7835c8a2e..7b1e52eb58 100644
--- a/firmware/buflib.c
+++ b/firmware/buflib.c
@@ -97,13 +97,34 @@
97 97
98#define BPANICF panicf 98#define BPANICF panicf
99 99
100/* Forward indices, used to index a block start pointer as block[fidx_XXX] */
101enum {
102 fidx_LEN, /* length of the block, must come first */
103 fidx_HANDLE, /* pointer to entry in the handle table */
104 fidx_OPS, /* pointer to an ops struct */
105 fidx_NAME, /* name, optional and variable length, must come last */
106};
107
108/* Backward indices, used to index a block end pointer as block[-bidx_XXX] */
109enum {
110 bidx_USER, /* dummy to get below fields to be 1-based */
111 bidx_CRC, /* CRC, protects all metadata behind it */
112 bidx_BSIZE, /* total size of the block header */
113};
114
115/* Number of fields in the block header, excluding the name, which is
116 * accounted for using the BSIZE field. Note that bidx_USER is not an
117 * actual field so it is not included in the count. */
118#define BUFLIB_NUM_FIELDS 5
119
100struct buflib_callbacks buflib_ops_locked = { 120struct buflib_callbacks buflib_ops_locked = {
101 .move_callback = NULL, 121 .move_callback = NULL,
102 .shrink_callback = NULL, 122 .shrink_callback = NULL,
103 .sync_callback = NULL, 123 .sync_callback = NULL,
104}; 124};
105 125
106#define IS_MOVABLE(a) (!a[2].ops || a[2].ops->move_callback) 126#define IS_MOVABLE(a) (!a[fidx_OPS].ops || a[fidx_OPS].ops->move_callback)
127
107static union buflib_data* find_first_free(struct buflib_context *ctx); 128static union buflib_data* find_first_free(struct buflib_context *ctx);
108static union buflib_data* find_block_before(struct buflib_context *ctx, 129static union buflib_data* find_block_before(struct buflib_context *ctx,
109 union buflib_data* block, 130 union buflib_data* block,
@@ -241,7 +262,7 @@ union buflib_data* handle_to_block(struct buflib_context* ctx, int handle)
241 if (!data) 262 if (!data)
242 return NULL; 263 return NULL;
243 264
244 return data - data[-2].val; 265 return data - data[-bidx_BSIZE].val;
245} 266}
246 267
247/* Shrink the handle table, returning true if its size was reduced, false if 268/* Shrink the handle table, returning true if its size was reduced, false if
@@ -263,6 +284,20 @@ static inline bool handle_table_shrink(struct buflib_context *ctx)
263 return handle != old_last; 284 return handle != old_last;
264} 285}
265 286
287static inline
288union buflib_data* userpointer_to_block_end(void *userpointer)
289{
290 union buflib_data *data = ALIGN_DOWN(userpointer, sizeof(*data));
291 return data;
292}
293
294static uint32_t calc_block_crc(union buflib_data *block,
295 union buflib_data *block_end)
296{
297 union buflib_data *crc_slot = &block_end[-bidx_CRC];
298 const size_t size = (crc_slot - block) * sizeof(*block);
299 return crc_32(block, size, 0xffffffff);
300}
266 301
267/* If shift is non-zero, it represents the number of places to move 302/* If shift is non-zero, it represents the number of places to move
268 * blocks in memory. Calculate the new address for this block, 303 * blocks in memory. Calculate the new address for this block,
@@ -275,32 +310,29 @@ static bool
275move_block(struct buflib_context* ctx, union buflib_data* block, int shift) 310move_block(struct buflib_context* ctx, union buflib_data* block, int shift)
276{ 311{
277 char* new_start; 312 char* new_start;
313 union buflib_data *new_block;
278 314
279 if (block < ctx->buf_start || block > ctx->alloc_end) 315 if (block < ctx->buf_start || block > ctx->alloc_end)
280 buflib_panic(ctx, "buflib data corrupted %p", block); 316 buflib_panic(ctx, "buflib data corrupted %p", block);
281 317
282 union buflib_data *new_block, *tmp = block[1].handle, *crc_slot; 318 union buflib_data *h_entry = block[fidx_HANDLE].handle;
283 struct buflib_callbacks *ops = block[2].ops; 319 union buflib_data *block_end = userpointer_to_block_end(h_entry->alloc);
284 crc_slot = (union buflib_data*)tmp->alloc - 1;
285 if (crc_slot < ctx->buf_start || crc_slot > ctx->alloc_end)
286 buflib_panic(ctx, "buflib metadata corrupted %p", crc_slot);
287 320
288 const int metadata_size = (crc_slot - block)*sizeof(union buflib_data); 321 uint32_t crc = calc_block_crc(block, block_end);
289 uint32_t crc = crc_32((void *)block, metadata_size, 0xffffffff); 322 if (crc != block_end[-bidx_CRC].crc)
290
291 /* check for metadata validity */
292 if (crc != crc_slot->crc)
293 buflib_panic(ctx, "buflib metadata corrupted, crc: 0x%08x, expected: 0x%08x", 323 buflib_panic(ctx, "buflib metadata corrupted, crc: 0x%08x, expected: 0x%08x",
294 (unsigned int)crc, (unsigned int)crc_slot->crc); 324 (unsigned int)crc, (unsigned int)block_end[-bidx_CRC].crc);
295 325
296 if (!IS_MOVABLE(block)) 326 if (!IS_MOVABLE(block))
297 return false; 327 return false;
298 328
299 int handle = ctx->handle_table - tmp; 329 int handle = ctx->handle_table - h_entry;
300 BDEBUGF("%s(): moving \"%s\"(id=%d) by %d(%d)\n", __func__, block[3].name, 330 BDEBUGF("%s(): moving \"%s\"(id=%d) by %d(%d)\n", __func__,
301 handle, shift, shift*(int)sizeof(union buflib_data)); 331 block[fidx_NAME].name, handle, shift, shift*(int)sizeof(union buflib_data));
302 new_block = block + shift; 332 new_block = block + shift;
303 new_start = tmp->alloc + shift*sizeof(union buflib_data); 333 new_start = h_entry->alloc + shift*sizeof(union buflib_data);
334
335 struct buflib_callbacks *ops = block[fidx_OPS].ops;
304 336
305 /* If move must be synchronized with use, user should have specified a 337 /* If move must be synchronized with use, user should have specified a
306 callback that handles this */ 338 callback that handles this */
@@ -308,10 +340,10 @@ move_block(struct buflib_context* ctx, union buflib_data* block, int shift)
308 ops->sync_callback(handle, true); 340 ops->sync_callback(handle, true);
309 341
310 bool retval = false; 342 bool retval = false;
311 if (!ops || ops->move_callback(handle, tmp->alloc, new_start) 343 if (!ops || ops->move_callback(handle, h_entry->alloc, new_start)
312 != BUFLIB_CB_CANNOT_MOVE) 344 != BUFLIB_CB_CANNOT_MOVE)
313 { 345 {
314 tmp->alloc = new_start; /* update handle table */ 346 h_entry->alloc = new_start; /* update handle table */
315 memmove(new_block, block, block->val * sizeof(union buflib_data)); 347 memmove(new_block, block, block->val * sizeof(union buflib_data));
316 retval = true; 348 retval = true;
317 } 349 }
@@ -424,41 +456,50 @@ buflib_compact_and_shrink(struct buflib_context *ctx, unsigned shrink_hints)
424 this < ctx->alloc_end; 456 this < ctx->alloc_end;
425 before = this, this += abs(this->val)) 457 before = this, this += abs(this->val))
426 { 458 {
427 if (this->val > 0 && this[2].ops 459 if (this->val <= 0)
428 && this[2].ops->shrink_callback) 460 continue;
461
462 struct buflib_callbacks *ops = this[fidx_OPS].ops;
463 if (!ops || !ops->shrink_callback)
464 continue;
465
466 union buflib_data* h_entry = this[fidx_HANDLE].handle;
467 int handle = ctx->handle_table - h_entry;
468
469 unsigned pos_hints = shrink_hints & BUFLIB_SHRINK_POS_MASK;
470 /* adjust what we ask for if there's free space in the front
471 * this isn't too unlikely assuming this block is
472 * shrinkable but not movable */
473 if (pos_hints == BUFLIB_SHRINK_POS_FRONT &&
474 before != this && before->val < 0)
429 { 475 {
430 int ret; 476 size_t free_space = (-before->val) * sizeof(union buflib_data);
431 int handle = ctx->handle_table - this[1].handle; 477 size_t wanted = shrink_hints & BUFLIB_SHRINK_SIZE_MASK;
432 char* data = this[1].handle->alloc; 478 if (wanted < free_space) /* no shrink needed? */
433 bool last = (this+this->val) == ctx->alloc_end; 479 continue;
434 unsigned pos_hints = shrink_hints & BUFLIB_SHRINK_POS_MASK; 480 wanted -= free_space;
435 /* adjust what we ask for if there's free space in the front 481 shrink_hints = pos_hints | wanted;
436 * this isn't too unlikely assuming this block is
437 * shrinkable but not movable */
438 if (pos_hints == BUFLIB_SHRINK_POS_FRONT
439 && before != this && before->val < 0)
440 {
441 size_t free_space = (-before->val) * sizeof(union buflib_data);
442 size_t wanted = shrink_hints & BUFLIB_SHRINK_SIZE_MASK;
443 if (wanted < free_space) /* no shrink needed? */
444 continue;
445 wanted -= free_space;
446 shrink_hints = pos_hints | wanted;
447 }
448 ret = this[2].ops->shrink_callback(handle, shrink_hints,
449 data, (char*)(this+this->val)-data);
450 result |= (ret == BUFLIB_CB_OK);
451 /* 'this' might have changed in the callback (if it shrinked
452 * from the top or even freed the handle), get it again */
453 this = handle_to_block(ctx, handle);
454 /* The handle was possibly be freed in the callback,
455 * re-run the loop with the handle before */
456 if (!this)
457 this = before;
458 /* could also change with shrinking from back */
459 else if (last)
460 ctx->alloc_end = this + this->val;
461 } 482 }
483
484 char* data = h_entry->alloc;
485 char* data_end = (char*)(this + this->val);
486 bool last = (data_end == (char*)ctx->alloc_end);
487
488 int ret = ops->shrink_callback(handle, shrink_hints,
489 data, data_end - data);
490 result |= (ret == BUFLIB_CB_OK);
491
492 /* 'this' might have changed in the callback (if it shrinked
493 * from the top or even freed the handle), get it again */
494 this = handle_to_block(ctx, handle);
495
496 /* The handle was possibly be freed in the callback,
497 * re-run the loop with the handle before */
498 if (!this)
499 this = before;
500 /* could also change with shrinking from back */
501 else if (last)
502 ctx->alloc_end = this + this->val;
462 } 503 }
463 /* shrinking was successful at least once, try compaction again */ 504 /* shrinking was successful at least once, try compaction again */
464 if (result) 505 if (result)
@@ -547,9 +588,7 @@ buflib_alloc_ex(struct buflib_context *ctx, size_t size, const char *name,
547 size += name_len; 588 size += name_len;
548 size = (size + sizeof(union buflib_data) - 1) / 589 size = (size + sizeof(union buflib_data) - 1) /
549 sizeof(union buflib_data) 590 sizeof(union buflib_data)
550 /* add 5 objects for alloc len, pointer to handle table entry and 591 + BUFLIB_NUM_FIELDS;
551 * name length, the ops pointer and crc */
552 + 5;
553handle_alloc: 592handle_alloc:
554 handle = handle_alloc(ctx); 593 handle = handle_alloc(ctx);
555 if (!handle) 594 if (!handle)
@@ -559,7 +598,7 @@ handle_alloc:
559 */ 598 */
560 union buflib_data* last_block = find_block_before(ctx, 599 union buflib_data* last_block = find_block_before(ctx,
561 ctx->alloc_end, false); 600 ctx->alloc_end, false);
562 struct buflib_callbacks* ops = last_block[2].ops; 601 struct buflib_callbacks* ops = last_block[fidx_OPS].ops;
563 unsigned hints = 0; 602 unsigned hints = 0;
564 if (!ops || !ops->shrink_callback) 603 if (!ops || !ops->shrink_callback)
565 { /* the last one isn't shrinkable 604 { /* the last one isn't shrinkable
@@ -627,23 +666,22 @@ buffer_alloc:
627 /* Set up the allocated block, by marking the size allocated, and storing 666 /* Set up the allocated block, by marking the size allocated, and storing
628 * a pointer to the handle. 667 * a pointer to the handle.
629 */ 668 */
630 union buflib_data *header_size_slot, *crc_slot; 669 block[fidx_LEN].val = size;
631 block->val = size; 670 block[fidx_HANDLE].handle = handle;
632 block[1].handle = handle; 671 block[fidx_OPS].ops = ops;
633 block[2].ops = ops;
634 if (name_len > 0) 672 if (name_len > 0)
635 strcpy(block[3].name, name); 673 strcpy(block[fidx_NAME].name, name);
636 header_size_slot = (union buflib_data*)B_ALIGN_UP(block[3].name + name_len); 674
637 header_size_slot->val = 5 + name_len/sizeof(union buflib_data); 675 size_t bsize = BUFLIB_NUM_FIELDS + name_len/sizeof(union buflib_data);
638 crc_slot = (union buflib_data*)(header_size_slot + 1); 676 union buflib_data *block_end = block + bsize;
639 crc_slot->crc = crc_32((void *)block, 677 block_end[-bidx_BSIZE].val = bsize;
640 (crc_slot - block)*sizeof(union buflib_data), 678 block_end[-bidx_CRC].crc = calc_block_crc(block, block_end);
641 0xffffffff); 679
642 handle->alloc = (char*)(crc_slot + 1); 680 handle->alloc = (char*)&block_end[-bidx_USER];
643 681
644 BDEBUGF("buflib_alloc_ex: size=%d handle=%p clb=%p crc=0x%0x name=\"%s\"\n", 682 BDEBUGF("buflib_alloc_ex: size=%d handle=%p clb=%p crc=0x%0x name=\"%s\"\n",
645 (unsigned int)size, (void *)handle, (void *)ops, 683 (unsigned int)size, (void *)handle, (void *)ops,
646 (unsigned int)crc_slot->crc, name ? block[3].name:""); 684 (unsigned int)block_end[-bidx_CRC].crc, name ? name : "");
647 685
648 block += size; 686 block += size;
649 /* alloc_end must be kept current if we're taking the last block. */ 687 /* alloc_end must be kept current if we're taking the last block. */
@@ -743,7 +781,7 @@ free_space_at_end(struct buflib_context* ctx)
743{ 781{
744 /* subtract 5 elements for 782 /* subtract 5 elements for
745 * val, handle, meta_len, ops and the handle table entry*/ 783 * val, handle, meta_len, ops and the handle table entry*/
746 ptrdiff_t diff = (ctx->last_handle - ctx->alloc_end - 5); 784 ptrdiff_t diff = (ctx->last_handle - ctx->alloc_end - BUFLIB_NUM_FIELDS);
747 diff -= 16; /* space for future handles */ 785 diff -= 16; /* space for future handles */
748 diff *= sizeof(union buflib_data); /* make it bytes */ 786 diff *= sizeof(union buflib_data); /* make it bytes */
749 diff -= 16; /* reserve 16 for the name */ 787 diff -= 16; /* reserve 16 for the name */
@@ -858,8 +896,6 @@ buflib_alloc_maximum(struct buflib_context* ctx, const char* name, size_t *size,
858bool 896bool
859buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t new_size) 897buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t new_size)
860{ 898{
861 union buflib_data *crc_slot;
862 int size_for_crc32;
863 char* oldstart = buflib_get_data(ctx, handle); 899 char* oldstart = buflib_get_data(ctx, handle);
864 char* newstart = new_start; 900 char* newstart = new_start;
865 char* newend = newstart + new_size; 901 char* newend = newstart + new_size;
@@ -883,9 +919,9 @@ buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t ne
883 metadata_size.val = aligned_oldstart - block; 919 metadata_size.val = aligned_oldstart - block;
884 /* update val and the handle table entry */ 920 /* update val and the handle table entry */
885 new_block = aligned_newstart - metadata_size.val; 921 new_block = aligned_newstart - metadata_size.val;
886 block[0].val = new_next_block - new_block; 922 block[fidx_LEN].val = new_next_block - new_block;
887 923
888 block[1].handle->alloc = newstart; 924 block[fidx_HANDLE].handle->alloc = newstart;
889 if (block != new_block) 925 if (block != new_block)
890 { 926 {
891 /* move metadata over, i.e. pointer to handle table entry and name 927 /* move metadata over, i.e. pointer to handle table entry and name
@@ -906,9 +942,9 @@ buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t ne
906 } 942 }
907 943
908 /* update crc of the metadata */ 944 /* update crc of the metadata */
909 crc_slot = (union buflib_data*)new_block[1].handle->alloc - 1; 945 union buflib_data *new_h_entry = new_block[fidx_HANDLE].handle;
910 size_for_crc32 = (crc_slot - new_block)*sizeof(union buflib_data); 946 union buflib_data *new_block_end = userpointer_to_block_end(new_h_entry->alloc);
911 crc_slot->crc = crc_32((void *)new_block, size_for_crc32, 0xffffffff); 947 new_block_end[-bidx_CRC].crc = calc_block_crc(new_block, new_block_end);
912 948
913 /* Now deal with size changes that create free blocks after the allocation */ 949 /* Now deal with size changes that create free blocks after the allocation */
914 if (old_next_block != new_next_block) 950 if (old_next_block != new_next_block)
@@ -932,12 +968,12 @@ buflib_shrink(struct buflib_context* ctx, int handle, void* new_start, size_t ne
932const char* buflib_get_name(struct buflib_context *ctx, int handle) 968const char* buflib_get_name(struct buflib_context *ctx, int handle)
933{ 969{
934 union buflib_data *data = ALIGN_DOWN(buflib_get_data(ctx, handle), sizeof (*data)); 970 union buflib_data *data = ALIGN_DOWN(buflib_get_data(ctx, handle), sizeof (*data));
935 size_t len = data[-2].val; 971 size_t len = data[-bidx_BSIZE].val;
936 if (len <= 5) 972 if (len <= BUFLIB_NUM_FIELDS)
937 return NULL; 973 return NULL;
938 974
939 data -= len; 975 data -= len;
940 return data[3].name; 976 return data[fidx_NAME].name;
941} 977}
942 978
943#ifdef DEBUG 979#ifdef DEBUG
@@ -952,10 +988,6 @@ void *buflib_get_data(struct buflib_context *ctx, int handle)
952 988
953void buflib_check_valid(struct buflib_context *ctx) 989void buflib_check_valid(struct buflib_context *ctx)
954{ 990{
955 union buflib_data *crc_slot;
956 int metadata_size;
957 uint32_t crc;
958
959 for(union buflib_data* this = ctx->buf_start; 991 for(union buflib_data* this = ctx->buf_start;
960 this < ctx->alloc_end; 992 this < ctx->alloc_end;
961 this += abs(this->val)) 993 this += abs(this->val))
@@ -963,14 +995,14 @@ void buflib_check_valid(struct buflib_context *ctx)
963 if (this->val < 0) 995 if (this->val < 0)
964 continue; 996 continue;
965 997
966 crc_slot = (union buflib_data*) 998 union buflib_data *h_entry = this[fidx_HANDLE].handle;
967 ((union buflib_data*)this[1].handle)->alloc - 1; 999 union buflib_data *block_end = userpointer_to_block_end(h_entry->alloc);
968 metadata_size = (crc_slot - this)*sizeof(union buflib_data); 1000 uint32_t crc = calc_block_crc(this, block_end);
969 crc = crc_32((void *)this, metadata_size, 0xffffffff); 1001 if (crc != block_end[-bidx_CRC].crc)
970 1002 {
971 if (crc != crc_slot->crc)
972 buflib_panic(ctx, "crc mismatch: 0x%08x, expected: 0x%08x", 1003 buflib_panic(ctx, "crc mismatch: 0x%08x, expected: 0x%08x",
973 (unsigned int)crc, (unsigned int)crc_slot->crc); 1004 (unsigned int)crc, (unsigned int)block_end[-bidx_CRC].crc);
1005 }
974 } 1006 }
975} 1007}
976#endif 1008#endif
@@ -985,16 +1017,11 @@ void buflib_print_allocs(struct buflib_context *ctx,
985 { 1017 {
986 if (!this->alloc) continue; 1018 if (!this->alloc) continue;
987 1019
988 int handle_num; 1020 int handle_num = end - this;
989 const char *name; 1021 void* alloc_start = this->alloc;
990 union buflib_data *block_start, *alloc_start; 1022 union buflib_data *block_start = handle_to_block(ctx, handle_num);
991 intptr_t alloc_len; 1023 const char* name = buflib_get_name(ctx, handle_num);
992 1024 intptr_t alloc_len = block_start[fidx_LEN];
993 handle_num = end - this;
994 alloc_start = buflib_get_data(ctx, handle_num);
995 name = buflib_get_name(ctx, handle_num);
996 block_start = (union buflib_data*)name - 3;
997 alloc_len = block_start->val * sizeof(union buflib_data);
998 1025
999 snprintf(buf, sizeof(buf), 1026 snprintf(buf, sizeof(buf),
1000 "%s(%d):\t%p\n" 1027 "%s(%d):\t%p\n"
@@ -1016,8 +1043,8 @@ void buflib_print_blocks(struct buflib_context *ctx,
1016 this += abs(this->val)) 1043 this += abs(this->val))
1017 { 1044 {
1018 snprintf(buf, sizeof(buf), "%8p: val: %4ld (%s)", 1045 snprintf(buf, sizeof(buf), "%8p: val: %4ld (%s)",
1019 this, this->val, 1046 this, this->val,
1020 this->val > 0? this[3].name:"<unallocated>"); 1047 this->val > 0 ? this[fidx_NAME].name : "<unallocated>");
1021 print(i++, buf); 1048 print(i++, buf);
1022 } 1049 }
1023} 1050}
@@ -1045,9 +1072,10 @@ void buflib_print_block_at(struct buflib_context *ctx, int block_num,
1045 this += abs(this->val); 1072 this += abs(this->val);
1046 block_num -= 1; 1073 block_num -= 1;
1047 } 1074 }
1075
1048 snprintf(buf, bufsize, "%8p: val: %4ld (%s)", 1076 snprintf(buf, bufsize, "%8p: val: %4ld (%s)",
1049 this, (long)this->val, 1077 this, (long)this->val,
1050 this->val > 0? this[3].name:"<unallocated>"); 1078 this->val > 0 ? this[fidx_NAME].name : "<unallocated>");
1051} 1079}
1052 1080
1053#endif 1081#endif