summaryrefslogtreecommitdiff
path: root/firmware/buflib_malloc.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/buflib_malloc.c')
-rw-r--r--firmware/buflib_malloc.c251
1 files changed, 251 insertions, 0 deletions
diff --git a/firmware/buflib_malloc.c b/firmware/buflib_malloc.c
new file mode 100644
index 0000000000..fdc2b5b925
--- /dev/null
+++ b/firmware/buflib_malloc.c
@@ -0,0 +1,251 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2023 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22/*
23 * Malloc backed buflib. This is intended for debugging rather than for
24 * serious use - the buffer passed to the context is wasted, and memory
25 * is acquired from malloc() instead. The main point is to make ASAN more
26 * effective by isolating buflib allocations from each other.
27 *
28 * Currently this is a bare-minimum implementation, it doesn't even run
29 * buflib callbacks since it never moves anything. It could later be
30 * extended with stress-testing options, for example by randomly moving
31 * allocations around.
32 */
33
34#include "buflib.h"
35#include "panic.h"
36#include <stdlib.h>
37
38static struct buflib_malloc_handle *get_free_handle(struct buflib_context *ctx)
39{
40 struct buflib_malloc_handle *h;
41 for (size_t i = 0; i < ctx->num_allocs; ++i)
42 {
43 h = &ctx->allocs[i];
44 if (h->size == 0)
45 return h;
46 }
47
48 ctx->num_allocs++;
49 ctx->allocs = realloc(ctx->allocs, ctx->num_allocs * sizeof(*ctx->allocs));
50 if (!ctx->allocs)
51 panicf("buflib %p handle OOM", ctx);
52
53 h = &ctx->allocs[ctx->num_allocs - 1];
54 h->size = 0;
55 return h;
56}
57
58static int get_handle_num(struct buflib_context *ctx,
59 struct buflib_malloc_handle *handle)
60{
61 return (handle - ctx->allocs) + 1;
62}
63
64static struct buflib_malloc_handle *get_handle(struct buflib_context *ctx,
65 int handle)
66{
67 return &ctx->allocs[handle - 1];
68}
69
70struct buflib_callbacks buflib_ops_locked = {
71 .move_callback = NULL,
72 .shrink_callback = NULL,
73 .sync_callback = NULL,
74};
75
76void buflib_init(struct buflib_context *ctx, void *buf, size_t size)
77{
78 ctx->allocs = NULL;
79 ctx->num_allocs = 0;
80 ctx->buf = buf;
81 ctx->bufsize = size;
82}
83
84size_t buflib_available(struct buflib_context *ctx)
85{
86 return ctx->bufsize;
87}
88
89size_t buflib_allocatable(struct buflib_context *ctx)
90{
91 return ctx->bufsize;
92}
93
94bool buflib_context_relocate(struct buflib_context *ctx, void *buf)
95{
96 ctx->buf = buf;
97 return true;
98}
99
100int buflib_alloc(struct buflib_context *ctx, size_t size)
101{
102 return buflib_alloc_ex(ctx, size, NULL);
103}
104
105int buflib_alloc_ex(struct buflib_context *ctx, size_t size,
106 struct buflib_callbacks *ops)
107{
108 struct buflib_malloc_handle *handle = get_free_handle(ctx);
109
110 handle->data = malloc(size);
111 handle->user = handle->data;
112 handle->size = size;
113 handle->pin_count = 0;
114 handle->ops = ops;
115
116 if (!handle->data)
117 panicf("buflib %p data OOM", ctx);
118
119 return get_handle_num(ctx, handle);
120}
121
122int buflib_alloc_maximum(struct buflib_context* ctx,
123 size_t *size, struct buflib_callbacks *ops)
124{
125 *size = ctx->bufsize;
126
127 return buflib_alloc_ex(ctx, *size, ops);
128}
129
130bool buflib_shrink(struct buflib_context *ctx, int handle,
131 void *newstart, size_t new_size)
132{
133 struct buflib_malloc_handle *h = get_handle(ctx, handle);
134 if (newstart < h->user || new_size > h->size - (newstart - h->user))
135 return false;
136
137 /* XXX: this might be allowed, but what would be the point... */
138 if (new_size == 0)
139 {
140 panicf("weird shrink");
141 return false;
142 }
143
144 /* due to buflib semantics we must not realloc */
145 h->user = newstart;
146 h->size = new_size;
147 return true;
148}
149
150void buflib_pin(struct buflib_context *ctx, int handle)
151{
152 struct buflib_malloc_handle *h = get_handle(ctx, handle);
153
154 h->pin_count++;
155}
156
157void buflib_unpin(struct buflib_context *ctx, int handle)
158{
159 struct buflib_malloc_handle *h = get_handle(ctx, handle);
160
161 h->pin_count--;
162}
163
164unsigned buflib_pin_count(struct buflib_context *ctx, int handle)
165{
166 struct buflib_malloc_handle *h = get_handle(ctx, handle);
167
168 return h->pin_count;
169}
170
171int buflib_free(struct buflib_context *ctx, int handle)
172{
173 if (handle <= 0)
174 return 0;
175
176 struct buflib_malloc_handle *h = get_handle(ctx, handle);
177
178 free(h->data);
179 h->size = 0;
180
181 return 0;
182}
183
184#ifdef BUFLIB_DEBUG_GET_DATA
185void *buflib_get_data(struct buflib_context *ctx, int handle)
186{
187 /* kind of silly since it's better for ASAN to catch this but... */
188 if (handle <= 0 || handle > ctx->num_allocs)
189 panicf("buflib %p: invalid handle %d", ctx, handle);
190
191 struct buflib_malloc_handle *h = get_handle(ctx, handle);
192 if (h->user == NULL)
193 panicf("buflib %p: handle %d use after free", ctx, handle);
194
195 return h->user;
196}
197#endif
198
199void *buflib_buffer_out(struct buflib_context *ctx, size_t *size)
200{
201 if (*size == 0)
202 *size = ctx->bufsize;
203
204 void *ret = ctx->buf;
205
206 ctx->buf += *size;
207 return ret;
208}
209
210void buflib_buffer_in(struct buflib_context *ctx, int size)
211{
212 ctx->buf -= size;
213}
214
215#ifdef BUFLIB_DEBUG_PRINT
216int buflib_get_num_blocks(struct buflib_context *ctx)
217{
218 return ctx->num_allocs;
219}
220
221bool buflib_print_block_at(struct buflib_context *ctx, int block_num,
222 char *buf, size_t bufsize)
223{
224 if (block_num >= ctx->num_allocs)
225 {
226 if (bufsize > 0)
227 *buf = '\0';
228 return false;
229 }
230
231 struct buflib_malloc_handle *handle = &ctx->allocs[block_num];
232 if (handle->data)
233 {
234 snprintf(buf, bufsize, "%03d addr:%8p length:%zu",
235 block_num, handle->data, handle->size);
236 }
237 else
238 {
239 snprintf(buf, bufsize, "%03d (unallocated)", block_num);
240 }
241
242 return true;
243}
244#endif
245
246#ifdef BUFLIB_DEBUG_CHECK_VALID
247void buflib_check_valid(struct buflib_context *ctx)
248{
249 (void)ctx;
250}
251#endif