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 #include <l4/re/c/rm.h> 24 25 #include "memory_incremental.h" 26 27 #include <ipc/cap_alloc.h> 28 #include <ipc/mem_ipc.h> 29 30 #include <stdlib.h> 31 32 33 34 /* Initialise the memory pool with an optional 'limit' in pages. */ 35 36 MemoryIncremental::MemoryIncremental(unsigned int limit, offset_t region_size, 37 l4_cap_idx_t dma) 38 : _limit(limit), _region_size(region_size), _dma(dma) 39 { 40 _limited = true; 41 } 42 43 MemoryIncremental::MemoryIncremental(l4_cap_idx_t dma) 44 : _region_size(PAGE_SIZE), _dma(dma) 45 { 46 _limited = false; 47 } 48 49 /* Allocate a block of the given 'size'. */ 50 51 Region *MemoryIncremental::allocate(offset_t size) 52 { 53 /* Attempt to allocate aligned memory. */ 54 55 void *current = NULL; 56 l4_cap_idx_t ds; 57 58 /* Make the size appropriate for the invocation. */ 59 60 size = round_multiple(size, PAGE_SIZE); 61 62 /* Use allocation permitting executable mapping of the memory. 63 NOTE: Here, it might be beneficial to employ an allocator that obtains 64 dataspaces and provides multiple blocks from each dataspace. */ 65 66 const l4_size_t attach_flags = L4RE_RM_F_SEARCH_ADDR | L4RE_RM_F_RWX; 67 const l4_size_t alloc_flags = l4_is_valid_cap(_dma) ? 68 L4RE_MA_CONTINUOUS | L4RE_MA_PINNED : 0; 69 70 long err = ipc_new_dataspace(size, alloc_flags, page_order(size), &ds); 71 72 if (err) 73 return NULL; 74 75 err = ipc_attach_dataspace_align(ds, size, attach_flags, page_order(size), 76 (void **) ¤t); 77 78 if (err) 79 return NULL; 80 81 /* Obtain physical addresses if a DMA space capability is available. */ 82 83 l4re_dma_space_dma_addr_t paddr = 0; 84 l4_size_t size_out = size; 85 86 if (l4_is_valid_cap(_dma)) 87 { 88 long err = l4re_dma_space_map(_dma, ds | L4_CAP_FPAGE_RW, 0, &size_out, 89 0, L4RE_DMA_SPACE_BIDIRECTIONAL, &paddr); 90 91 if (err) 92 return NULL; 93 94 if (size_out < size) 95 return NULL; 96 } 97 98 return new Region((offset_t) current, (offset_t) current + size, paddr); 99 } 100 101 /* Deallocate the given region. */ 102 103 void MemoryIncremental::deallocate(Region *region) 104 { 105 l4_cap_idx_t cap; 106 107 ipc_detach_dataspace((void *) region->start, &cap); 108 109 if (l4_is_valid_cap(cap)) 110 ipc_cap_free_um(cap); 111 112 delete region; 113 } 114 115 /* Allocate a new region of the given 'size' rounded to the nearest page. */ 116 117 Region *MemoryIncremental::region(offset_t size) 118 { 119 std::lock_guard<std::mutex> guard(_lock); 120 121 offset_t rounded = round(size, PAGE_SIZE); 122 offset_t pages = rounded / PAGE_SIZE; 123 124 /* Check for sufficient pages. */ 125 126 if (!_limited || (_limit >= pages)) 127 { 128 /* Attempt to allocate aligned memory. */ 129 130 Region *region = allocate(rounded); 131 132 if (region == NULL) 133 return NULL; 134 135 if (_limited) 136 _limit -= pages; 137 138 return region; 139 } 140 141 /* Return no region without sufficient pages. */ 142 143 else 144 return NULL; 145 } 146 147 /* Allocate a new region having the default region size rounded to the nearest 148 page. */ 149 150 Region *MemoryIncremental::region() 151 { 152 return region(_region_size); 153 } 154 155 /* Release the allocated 'region'. */ 156 157 void MemoryIncremental::release(Region *region) 158 { 159 std::lock_guard<std::mutex> guard(_lock); 160 161 offset_t rounded = round(region->size(), PAGE_SIZE); 162 offset_t pages = rounded / PAGE_SIZE; 163 164 if (_limited) 165 _limit += pages; 166 167 deallocate(region); 168 } 169 170 // vim: tabstop=4 expandtab shiftwidth=4