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