xref: /kvm-unit-tests/lib/alloc.c (revision c88b7beaafd711e0ca98529a56c9d35eedc60b5f)
1 #include "alloc.h"
2 #include "asm/page.h"
3 #include "bitops.h"
4 
5 void *malloc(size_t size)
6 {
7 	return memalign(sizeof(long), size);
8 }
9 
10 static bool mult_overflow(size_t a, size_t b)
11 {
12 #if BITS_PER_LONG == 32
13 	/* 32 bit system, easy case: just use u64 */
14 	return (u64)a * (u64)b >= (1ULL << 32);
15 #else
16 #ifdef __SIZEOF_INT128__
17 	/* if __int128 is available use it (like the u64 case above) */
18 	unsigned __int128 res = a;
19 	res *= b;
20 	res >>= 64;
21 	return res != 0;
22 #else
23 	u64 tmp;
24 
25 	if ((a >> 32) && (b >> 32))
26 		return true;
27 	if (!(a >> 32) && !(b >> 32))
28 		return false;
29 	tmp = (u32)a;
30 	tmp *= (u32)b;
31 	tmp >>= 32;
32 	if (a < b)
33 		tmp += a * (b >> 32);
34 	else
35 		tmp += b * (a >> 32);
36 	return tmp >> 32;
37 #endif /* __SIZEOF_INT128__ */
38 #endif /* BITS_PER_LONG == 32 */
39 }
40 
41 void *calloc(size_t nmemb, size_t size)
42 {
43 	void *ptr;
44 
45 	assert(!mult_overflow(nmemb, size));
46 	ptr = malloc(nmemb * size);
47 	if (ptr)
48 		memset(ptr, 0, nmemb * size);
49 	return ptr;
50 }
51 
52 #define METADATA_EXTRA	(2 * sizeof(uintptr_t))
53 #define OFS_SLACK	(-2 * sizeof(uintptr_t))
54 #define OFS_SIZE	(-sizeof(uintptr_t))
55 
56 static inline void *block_begin(void *mem)
57 {
58 	uintptr_t slack = *(uintptr_t *)(mem + OFS_SLACK);
59 	return mem - slack;
60 }
61 
62 static inline uintptr_t block_size(void *mem)
63 {
64 	return *(uintptr_t *)(mem + OFS_SIZE);
65 }
66 
67 void free(void *ptr)
68 {
69 	if (!alloc_ops->free)
70 		return;
71 
72 	void *base = block_begin(ptr);
73 	uintptr_t sz = block_size(ptr);
74 
75 	alloc_ops->free(base, sz);
76 }
77 
78 void *memalign(size_t alignment, size_t size)
79 {
80 	void *p;
81 	uintptr_t blkalign;
82 	uintptr_t mem;
83 
84 	if (!size)
85 		return NULL;
86 
87 	assert(alignment >= sizeof(void *) && is_power_of_2(alignment));
88 	assert(alloc_ops && alloc_ops->memalign);
89 
90 	size += alignment - 1;
91 	blkalign = MAX(alignment, alloc_ops->align_min);
92 	size = ALIGN(size + METADATA_EXTRA, alloc_ops->align_min);
93 	p = alloc_ops->memalign(blkalign, size);
94 	assert(p);
95 
96 	/* Leave room for metadata before aligning the result.  */
97 	mem = (uintptr_t)p + METADATA_EXTRA;
98 	mem = ALIGN(mem, alignment);
99 
100 	/* Write the metadata */
101 	*(uintptr_t *)(mem + OFS_SLACK) = mem - (uintptr_t)p;
102 	*(uintptr_t *)(mem + OFS_SIZE) = size;
103 	return (void *)mem;
104 }
105