1 /* 2 * A system pager implementation residing in the same task as a program. 3 * 4 * Copyright (C) 2022 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/env.h> 23 #include <l4/sys/err.h> 24 #include <l4/util/util.h> 25 26 #include <ipc/mem_ipc.h> 27 #include <mem/memory_utils.h> 28 29 #include <stdio.h> 30 #include <string.h> 31 32 #include "dataspace_client.h" 33 #include "internal_pager.h" 34 35 36 37 /* A simple system pager also acting as a region mapper. */ 38 39 /* Add a region to the pager. */ 40 41 void InternalPager::add(ExecRegion ®ion) 42 { 43 _regions[region.start] = region; 44 } 45 46 /* Handle a general exception. */ 47 48 long InternalPager::exception(l4_exc_regs_t regs, l4_snd_fpage_t *region) 49 { 50 (void) region; 51 52 printf("exception(...) -> pfa = %lx, pc = %lx\n", l4_utcb_exc_pfa(®s), l4_utcb_exc_pc(®s)); 53 54 #if 0 55 printf("r15 = %lx\n", regs.r15); 56 printf("r14 = %lx\n", regs.r14); 57 printf("r13 = %lx\n", regs.r13); 58 printf("r12 = %lx\n", regs.r12); 59 printf("r11 = %lx\n", regs.r11); 60 printf("r10 = %lx\n", regs.r10); 61 printf("r9 = %lx\n", regs.r9); 62 printf("r8 = %lx\n", regs.r8); 63 printf("rdi = %lx\n", regs.rdi); 64 printf("rsi = %lx\n", regs.rsi); 65 printf("rbp = %lx\n", regs.rbp); 66 printf("pfa = %lx\n", regs.pfa); 67 printf("rbx = %lx\n", regs.rbx); 68 printf("rdx = %lx\n", regs.rdx); 69 printf("rcx = %lx\n", regs.rcx); 70 printf("rax = %lx\n", regs.rax); 71 printf("trapno = %lx\n", regs.trapno); 72 printf("err = %lx\n", regs.err); 73 printf("ip = %lx\n", regs.ip); 74 printf("flags = %lx\n", regs.flags); 75 printf("sp = %lx\n", regs.sp); 76 printf("ss = %lx\n", regs.ss); 77 printf("fs_base = %lx\n", regs.fs_base); 78 printf("gs_base = %lx\n", regs.gs_base); 79 #endif 80 81 while (1) 82 l4_sleep_forever(); 83 84 return L4_EOK; 85 } 86 87 #define DEBUG 0 88 89 /* Handle a page fault using any configured regions. */ 90 91 long InternalPager::page_fault(l4_umword_t pfa, l4_umword_t pc, l4_snd_fpage_t *region) 92 { 93 l4_umword_t addr = pfa & ~7UL, flags = pfa & 7; 94 95 #if DEBUG 96 printf("page_fault(%lx, %lx) -> %lx (%lx) -> ", pfa, pc, addr, flags); 97 #endif 98 99 ExecRegions::iterator it = _regions.upper_bound(addr); 100 101 if (it != _regions.begin()) 102 it--; 103 else 104 { 105 printf("not mapped!\n"); 106 return -L4_ENOMEM; 107 } 108 109 ExecRegion &r = it->second; 110 111 if ((addr >= r.start) && (addr < r.start + r.size)) 112 { 113 offset_t window_size = L4_PAGESIZE; 114 address_t window_base = trunc(addr, window_size); 115 offset_t offset = addr - r.start; 116 address_t page_addr = trunc(addr, L4_PAGESIZE); 117 address_t hot_spot = page_addr - window_base; 118 119 /* Interact with the region's dataspace, specifying a receive window for a 120 map operation. Here, a single page is specified. */ 121 122 client_Dataspace dataspace(r.ds); 123 l4_snd_fpage_t rw_region = {0, l4_fpage(window_base, L4_PAGESHIFT, 0)}; 124 map_flags_t map_flags = (flags & 4 ? L4RE_DS_F_RX : 0) | (flags & 2 ? L4RE_DS_F_W : 0) | (flags & 1 ? L4RE_DS_F_R : 0); 125 126 if (!map_flags) 127 map_flags = L4RE_DS_F_R; 128 129 #if DEBUG 130 printf("window_base = %lx; window_size = %lx\n", window_base, window_size); 131 printf("region = {%lx, {%lx, %d}}\n", rw_region.snd_base, l4_fpage_memaddr(rw_region.fpage), l4_fpage_size(rw_region.fpage)); 132 printf("map(%lx, %lx, %lx) -> %lx\n", offset, hot_spot, map_flags, r.ds); 133 #endif 134 135 long err = dataspace.map(offset, hot_spot, map_flags & r.flags, &rw_region); 136 137 /* Indicate an unspecified result, since the mapping should have taken 138 place. */ 139 140 *region = {0, l4_fpage_invalid()}; 141 142 return err; 143 } 144 145 #if DEBUG 146 printf("not mapped!\n"); 147 #endif 148 149 return -L4_ENOMEM; 150 } 151 152 /* Attach a region for provision when page faults occur. This is required in 153 the initialisation of a program by the C library which requires a region 154 mapper. */ 155 156 long InternalPager::attach(address_t *start, offset_t size, map_flags_t flags, 157 l4_cap_idx_t ds, address_t offset, 158 unsigned char align) 159 { 160 #if DEBUG 161 printf("attach(%lx, %ld, %lx, ..., %lx, %d)\n", *start, size, flags, offset, align); 162 #endif 163 164 if (align < L4_PAGESHIFT) 165 align = L4_PAGESHIFT; 166 167 offset_t increment = 1UL << align; 168 offset_t region_size = round(size, increment); 169 170 /* Either attempt to find an address for the specified region, starting from 171 any indicated address. */ 172 173 if (flags & L4RE_RM_F_SEARCH_ADDR) 174 { 175 address_t region_start = trunc(*start, increment); 176 ExecRegions::iterator it = _regions.upper_bound(*start); 177 178 if (!region_start) 179 region_start += increment; 180 181 #if DEBUG 182 printf("-> search from %lx -> %lx...\n", *start, region_start); 183 #endif 184 185 /* Before last known region. */ 186 187 while (it != _regions.end()) 188 { 189 ExecRegions::iterator next = it; 190 ExecRegion &r = it->second; 191 address_t start_limit; 192 address_t end_limit = r.start; 193 194 /* Consider any preceding region. If no such region exists, choose an 195 address at the start of memory. */ 196 197 if (it == _regions.begin()) 198 start_limit = L4_PAGESIZE; 199 else 200 { 201 it--; 202 ExecRegion &pr = it->second; 203 start_limit = pr.start + pr.size; 204 it = next; 205 } 206 207 /* Test against the limits. */ 208 209 if (region_start < start_limit) 210 region_start = round(start_limit, increment); 211 212 /* Investigate subsequent regions if not enough space exists between the 213 preceding region (or start of memory) and the current region. */ 214 215 if ((region_start + region_size) > end_limit) 216 { 217 it++; 218 if (it == _regions.end()) 219 return -L4_ENOMEM; 220 } 221 else 222 break; 223 } 224 225 #if DEBUG 226 printf("-> added region at %lx size %ld (%d) ds %lx\n", region_start, region_size, page_order(region_size), ds); 227 #endif 228 229 ExecRegion r = (ExecRegion) {region_start, region_size, flags & L4RE_DS_F_RIGHTS_MASK, ds}; 230 231 add(r); 232 233 *start = region_start; 234 return L4_EOK; 235 } 236 237 /* Or attempt to add the specified region at a specific address. */ 238 239 else 240 { 241 // NOTE: To be implemented. 242 243 #if DEBUG 244 printf("-> region of size %ld (%d) not added!\n", region_size, page_order(region_size)); 245 #endif 246 247 return -L4_ENOMEM; 248 } 249 } 250 251 /* vim: tabstop=2 expandtab shiftwidth=2 252 */