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