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