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 return L4_EOK; 80 } 81 82 #define DEBUG 0 83 84 /* Handle a page fault using any configured regions. */ 85 86 long InternalPager::page_fault(l4_umword_t pfa, l4_umword_t pc, l4_snd_fpage_t *region) 87 { 88 l4_umword_t addr = pfa & ~7UL, flags = pfa & 7; 89 90 #if DEBUG 91 printf("page_fault(%lx, %lx) -> %lx (%lx) -> ", pfa, pc, addr, flags); 92 #endif 93 94 ExecRegions::iterator it = _regions.upper_bound(addr); 95 96 if (it != _regions.begin()) 97 it--; 98 else 99 { 100 printf("not mapped!\n"); 101 return -L4_ENOMEM; 102 } 103 104 ExecRegion &r = it->second; 105 106 if ((addr >= r.start) && (addr < r.start + r.size)) 107 { 108 l4_addr_t page_addr = trunc(addr, L4_PAGESIZE); 109 110 /* Interact with the region's dataspace, specifying a receive window for a 111 map operation. Here, a single page is specified. */ 112 113 client_Dataspace dataspace(r.ds); 114 l4_snd_fpage_t region = {0, l4_fpage(page_addr, L4_PAGESHIFT, 0)}; 115 116 #if DEBUG 117 printf("region = {%lx, {%lx, %d}}\n", region.snd_base, l4_fpage_memaddr(region.fpage), l4_fpage_size(region.fpage)); 118 #endif 119 120 return dataspace.map(0, 0, L4_FPAGE_RO, ®ion); 121 } 122 123 #if DEBUG 124 printf("not mapped!\n"); 125 #endif 126 127 return -L4_ENOMEM; 128 } 129 130 /* Attach a region for provision when page faults occur. This is required in 131 the initialisation of a program by the C library which requires a region 132 mapper. */ 133 134 long InternalPager::attach(address_t *start, offset_t size, map_flags_t flags, 135 l4_cap_idx_t ds, address_t offset, 136 unsigned char align) 137 { 138 #if DEBUG 139 printf("attach(%lx, %ld, %lx, ..., %lx, %d)\n", *start, size, flags, offset, align); 140 #endif 141 142 if (align < L4_PAGESHIFT) 143 align = L4_PAGESHIFT; 144 145 offset_t increment = 1UL << align; 146 offset_t region_size = round(size, increment); 147 148 /* Either attempt to find an address for the specified region, starting from 149 any indicated address. */ 150 151 if (flags & L4RE_RM_F_SEARCH_ADDR) 152 { 153 address_t region_start = trunc(*start, increment); 154 ExecRegions::iterator it = _regions.upper_bound(*start); 155 156 if (!region_start) 157 region_start += increment; 158 159 #if DEBUG 160 printf("-> search from %lx -> %lx...\n", *start, region_start); 161 #endif 162 163 /* Before last known region. */ 164 165 while (it != _regions.end()) 166 { 167 ExecRegions::iterator next = it; 168 ExecRegion &r = it->second; 169 address_t start_limit; 170 address_t end_limit = r.start; 171 172 /* Consider any preceding region. If no such region exists, choose an 173 address at the start of memory. */ 174 175 if (it == _regions.begin()) 176 start_limit = L4_PAGESIZE; 177 else 178 { 179 it--; 180 ExecRegion &pr = it->second; 181 start_limit = pr.start + pr.size; 182 it = next; 183 } 184 185 /* Test against the limits. */ 186 187 if (region_start < start_limit) 188 region_start = round(start_limit, increment); 189 190 /* Investigate subsequent regions if not enough space exists between the 191 preceding region (or start of memory) and the current region. */ 192 193 if ((region_start + region_size) > end_limit) 194 { 195 it++; 196 if (it == _regions.end()) 197 return -L4_ENOMEM; 198 } 199 else 200 break; 201 } 202 203 #if DEBUG 204 printf("-> added region for %lx size %ld (%d)\n", region_start, region_size, page_order(region_size)); 205 #endif 206 207 ExecRegion r = (ExecRegion) {region_start, region_size, flags & L4RE_DS_F_RIGHTS_MASK, ds}; 208 209 add(r); 210 211 *start = region_start; 212 return L4_EOK; 213 } 214 215 /* Or attempt to add the specified region at a specific address. */ 216 217 else 218 { 219 // NOTE: To be implemented. 220 221 #if DEBUG 222 printf("-> region of size %ld (%d) not added!\n", region_size, page_order(region_size)); 223 #endif 224 225 return -L4_ENOMEM; 226 } 227 } 228 229 /* vim: tabstop=2 expandtab shiftwidth=2 230 */