1 // © 2021 Qualcomm Innovation Center, Inc. All rights reserved.
2 //
3 // SPDX-License-Identifier: BSD-3-Clause
4 
5 #include <assert.h>
6 #include <hyptypes.h>
7 
8 #include <bootmem.h>
9 #include <util.h>
10 
11 #include "event_handlers.h"
12 
13 static bootmem_allocator_t bootmem_allocator;
14 
15 // For now the hypervisor private heap is statically defined in the linker
16 // script. The intention is to replace this with dynamically determined memory -
17 // such as through boot configuration structures.
18 extern uint8_t heap_private_start;
19 extern uint8_t heap_private_end;
20 extern uint8_t image_virt_end;
21 
22 void
allocator_boot_handle_boot_runtime_first_init(void)23 allocator_boot_handle_boot_runtime_first_init(void)
24 {
25 	assert((uintptr_t)&heap_private_end > (uintptr_t)&heap_private_start);
26 
27 	static_assert(
28 		PLATFORM_HEAP_PRIVATE_SIZE <= PLATFORM_RW_DATA_SIZE,
29 		"PLATFORM_HEAP_PRIVATE_SIZE must be <= PLATFORM_RW_DATA_SIZE");
30 	static_assert(PLATFORM_RW_DATA_SIZE >= 0x200000,
31 		      "PLATFORM_RW_DATA_SIZE must be >= 2MB");
32 
33 	// We only give heap within the first 2MB RW page to the bootmem. We
34 	// will map the rest of the heap during the hyp_aspace init.
35 	void	 *base	  = &heap_private_start;
36 	uintptr_t map_end = util_balign_up((uintptr_t)base, 0x200000U);
37 
38 	uintptr_t hyp_priv_end =
39 		(((uintptr_t)&image_virt_end) - (size_t)PLATFORM_RW_DATA_SIZE) +
40 		(size_t)PLATFORM_HEAP_PRIVATE_SIZE;
41 	uintptr_t end = util_min(map_end, hyp_priv_end);
42 
43 	assert((uintptr_t)base < end);
44 	size_t size = end - (uintptr_t)base;
45 
46 	assert(base != NULL);
47 	assert(size >= 0x1000U);
48 
49 	bootmem_allocator.pool_base    = (uint8_t *)base;
50 	bootmem_allocator.pool_size    = size;
51 	bootmem_allocator.alloc_offset = 0;
52 }
53 
54 void_ptr_result_t
bootmem_allocate(size_t size,size_t align)55 bootmem_allocate(size_t size, size_t align)
56 {
57 	assert(bootmem_allocator.alloc_offset <= bootmem_allocator.pool_size);
58 	uintptr_t loc = (uintptr_t)bootmem_allocator.pool_base;
59 
60 	assert(!util_add_overflows(loc, bootmem_allocator.alloc_offset));
61 	loc += bootmem_allocator.alloc_offset;
62 
63 	if (!util_is_p2_or_zero(align)) {
64 		return void_ptr_result_error(ERROR_ARGUMENT_ALIGNMENT);
65 	}
66 	if (util_is_p2(align)) {
67 		loc = util_balign_up(loc, align);
68 	}
69 
70 	size_t free_boot = bootmem_allocator.pool_size -
71 			   (loc - (uintptr_t)bootmem_allocator.pool_base);
72 
73 	if (size > free_boot) {
74 		return void_ptr_result_error(ERROR_NOMEM);
75 	}
76 
77 	bootmem_allocator.alloc_offset =
78 		(loc - (uintptr_t)bootmem_allocator.pool_base + size);
79 
80 	return void_ptr_result_ok((void *)loc);
81 }
82 
83 void_ptr_result_t
bootmem_allocate_remaining(size_t * size)84 bootmem_allocate_remaining(size_t *size)
85 {
86 	assert(size != NULL);
87 
88 	assert(bootmem_allocator.alloc_offset <= bootmem_allocator.pool_size);
89 	size_t free_boot =
90 		bootmem_allocator.pool_size - bootmem_allocator.alloc_offset;
91 	if (free_boot == 0U) {
92 		return void_ptr_result_error(ERROR_NOMEM);
93 	}
94 	*size = free_boot;
95 
96 	uintptr_t loc = (uintptr_t)bootmem_allocator.pool_base;
97 	assert(!util_add_overflows(loc, bootmem_allocator.alloc_offset));
98 	loc += bootmem_allocator.alloc_offset;
99 
100 	bootmem_allocator.alloc_offset = bootmem_allocator.pool_size;
101 
102 	return void_ptr_result_ok((void *)loc);
103 }
104 
105 void *
bootmem_get_region(size_t * size)106 bootmem_get_region(size_t *size)
107 {
108 	*size = bootmem_allocator.pool_size;
109 	return bootmem_allocator.pool_base;
110 }
111