1 /* 2 * A memory pool allocating a region at a time from the system. 3 * 4 * Copyright (C) 2021, 2022, 2024 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <l4/re/c/mem_alloc.h> 23 24 #include "memory_incremental.h" 25 26 #include <ipc/mem_ipc.h> 27 28 #include <stdlib.h> 29 30 31 32 /* Initialise the memory pool with an optional 'limit' in pages. */ 33 34 MemoryIncremental::MemoryIncremental(unsigned int limit, offset_t region_size, 35 l4_cap_idx_t dma) 36 : _limit(limit), _region_size(region_size), _dma(dma) 37 { 38 _limited = true; 39 } 40 41 MemoryIncremental::MemoryIncremental(l4_cap_idx_t dma) 42 : _region_size(PAGE_SIZE), _dma(dma) 43 { 44 _limited = false; 45 } 46 47 /* Allocate a block of the given 'size'. */ 48 49 Region *MemoryIncremental::allocate(offset_t size) 50 { 51 /* Attempt to allocate aligned memory. */ 52 53 void *current = NULL; 54 l4re_ds_t ds; 55 56 /* Make the size appropriate for the invocation. */ 57 58 size = round_multiple(size, PAGE_SIZE); 59 60 /* Use allocation permitting executable mapping of the memory. 61 NOTE: Here, it might be beneficial to employ an allocator that obtains 62 dataspaces and provides multiple blocks from each dataspace. */ 63 64 const l4_size_t attach_flags = L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RWX; 65 const l4_size_t alloc_flags = l4_is_valid_cap(_dma) ? 66 L4RE_MA_CONTINUOUS | L4RE_MA_PINNED : 0; 67 68 long err = ipc_new_dataspace(size, alloc_flags, page_order(size), &ds); 69 70 if (err) 71 return NULL; 72 73 err = ipc_attach_dataspace_align(ds, size, attach_flags, page_order(size), 74 (void **) ¤t); 75 76 if (err) 77 return NULL; 78 79 /* Obtain physical addresses if a DMA space capability is available. */ 80 81 l4re_dma_space_dma_addr_t paddr = 0; 82 l4_size_t size_out = size; 83 84 if (l4_is_valid_cap(_dma)) 85 { 86 long err = l4re_dma_space_map(_dma, ds | L4_CAP_FPAGE_RW, 0, &size_out, 87 0, L4RE_DMA_SPACE_BIDIRECTIONAL, &paddr); 88 89 if (err) 90 return NULL; 91 92 if (size_out < size) 93 return NULL; 94 } 95 96 return new Region((offset_t) current, (offset_t) current + size, paddr); 97 } 98 99 /* Deallocate the given region. */ 100 101 void MemoryIncremental::deallocate(Region *region) 102 { 103 ipc_detach_dataspace((void *) region->start); 104 delete region; 105 } 106 107 /* Allocate a new region of the given 'size' rounded to the nearest page. */ 108 109 Region *MemoryIncremental::region(offset_t size) 110 { 111 std::lock_guard<std::mutex> guard(_lock); 112 113 offset_t rounded = round(size, PAGE_SIZE); 114 offset_t pages = rounded / PAGE_SIZE; 115 116 /* Check for sufficient pages. */ 117 118 if (!_limited || (_limit >= pages)) 119 { 120 /* Attempt to allocate aligned memory. */ 121 122 Region *region = allocate(rounded); 123 124 if (region == NULL) 125 return NULL; 126 127 if (_limited) 128 _limit -= pages; 129 130 return region; 131 } 132 133 /* Return no region without sufficient pages. */ 134 135 else 136 return NULL; 137 } 138 139 /* Allocate a new region having the default region size rounded to the nearest 140 page. */ 141 142 Region *MemoryIncremental::region() 143 { 144 return region(_region_size); 145 } 146 147 /* Release the allocated 'region'. */ 148 149 void MemoryIncremental::release(Region *region) 150 { 151 std::lock_guard<std::mutex> guard(_lock); 152 153 offset_t rounded = round(region->size(), PAGE_SIZE); 154 offset_t pages = rounded / PAGE_SIZE; 155 156 if (_limited) 157 _limit += pages; 158 159 deallocate(region); 160 } 161 162 // vim: tabstop=4 expandtab shiftwidth=4