1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/libexec/lib/src/internal_pager.cc Sun Jun 12 18:56:25 2022 +0200
1.3 @@ -0,0 +1,230 @@
1.4 +/*
1.5 + * A system pager implementation residing in the same task as a program.
1.6 + *
1.7 + * Copyright (C) 2022 Paul Boddie <paul@boddie.org.uk>
1.8 + *
1.9 + * This program is free software; you can redistribute it and/or
1.10 + * modify it under the terms of the GNU General Public License as
1.11 + * published by the Free Software Foundation; either version 2 of
1.12 + * the License, or (at your option) any later version.
1.13 + *
1.14 + * This program is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program; if not, write to the Free Software
1.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.22 + * Boston, MA 02110-1301, USA
1.23 + */
1.24 +
1.25 +#include <l4/re/env.h>
1.26 +#include <l4/sys/err.h>
1.27 +#include <l4/util/util.h>
1.28 +
1.29 +#include <ipc/mem_ipc.h>
1.30 +#include <mem/memory_utils.h>
1.31 +
1.32 +#include <stdio.h>
1.33 +#include <string.h>
1.34 +
1.35 +#include "dataspace_client.h"
1.36 +#include "internal_pager.h"
1.37 +
1.38 +
1.39 +
1.40 +/* A simple system pager also acting as a region mapper. */
1.41 +
1.42 +/* Add a region to the pager. */
1.43 +
1.44 +void InternalPager::add(ExecRegion ®ion)
1.45 +{
1.46 + _regions[region.start] = region;
1.47 +}
1.48 +
1.49 +/* Handle a general exception. */
1.50 +
1.51 +long InternalPager::exception(l4_exc_regs_t regs, l4_snd_fpage_t *region)
1.52 +{
1.53 + (void) region;
1.54 +
1.55 + printf("exception(...) -> pfa = %lx, pc = %lx\n", l4_utcb_exc_pfa(®s), l4_utcb_exc_pc(®s));
1.56 +
1.57 + printf("r15 = %lx\n", regs.r15);
1.58 + printf("r14 = %lx\n", regs.r14);
1.59 + printf("r13 = %lx\n", regs.r13);
1.60 + printf("r12 = %lx\n", regs.r12);
1.61 + printf("r11 = %lx\n", regs.r11);
1.62 + printf("r10 = %lx\n", regs.r10);
1.63 + printf("r9 = %lx\n", regs.r9);
1.64 + printf("r8 = %lx\n", regs.r8);
1.65 + printf("rdi = %lx\n", regs.rdi);
1.66 + printf("rsi = %lx\n", regs.rsi);
1.67 + printf("rbp = %lx\n", regs.rbp);
1.68 + printf("pfa = %lx\n", regs.pfa);
1.69 + printf("rbx = %lx\n", regs.rbx);
1.70 + printf("rdx = %lx\n", regs.rdx);
1.71 + printf("rcx = %lx\n", regs.rcx);
1.72 + printf("rax = %lx\n", regs.rax);
1.73 + printf("trapno = %lx\n", regs.trapno);
1.74 + printf("err = %lx\n", regs.err);
1.75 + printf("ip = %lx\n", regs.ip);
1.76 + printf("flags = %lx\n", regs.flags);
1.77 + printf("sp = %lx\n", regs.sp);
1.78 + printf("ss = %lx\n", regs.ss);
1.79 + printf("fs_base = %lx\n", regs.fs_base);
1.80 + printf("gs_base = %lx\n", regs.gs_base);
1.81 +
1.82 + return L4_EOK;
1.83 +}
1.84 +
1.85 +#define DEBUG 0
1.86 +
1.87 +/* Handle a page fault using any configured regions. */
1.88 +
1.89 +long InternalPager::page_fault(l4_umword_t pfa, l4_umword_t pc, l4_snd_fpage_t *region)
1.90 +{
1.91 + l4_umword_t addr = pfa & ~7UL, flags = pfa & 7;
1.92 +
1.93 +#if DEBUG
1.94 + printf("page_fault(%lx, %lx) -> %lx (%lx) -> ", pfa, pc, addr, flags);
1.95 +#endif
1.96 +
1.97 + ExecRegions::iterator it = _regions.upper_bound(addr);
1.98 +
1.99 + if (it != _regions.begin())
1.100 + it--;
1.101 + else
1.102 + {
1.103 + printf("not mapped!\n");
1.104 + return -L4_ENOMEM;
1.105 + }
1.106 +
1.107 + ExecRegion &r = it->second;
1.108 +
1.109 + if ((addr >= r.start) && (addr < r.start + r.size))
1.110 + {
1.111 + l4_addr_t page_addr = trunc(addr, L4_PAGESIZE);
1.112 +
1.113 + /* Interact with the region's dataspace, specifying a receive window for a
1.114 + map operation. Here, a single page is specified. */
1.115 +
1.116 + client_Dataspace dataspace(r.ds);
1.117 + l4_snd_fpage_t region = {0, l4_fpage(page_addr, L4_PAGESHIFT, 0)};
1.118 +
1.119 +#if DEBUG
1.120 + printf("region = {%lx, {%lx, %d}}\n", region.snd_base, l4_fpage_memaddr(region.fpage), l4_fpage_size(region.fpage));
1.121 +#endif
1.122 +
1.123 + return dataspace.map(0, 0, L4_FPAGE_RO, ®ion);
1.124 + }
1.125 +
1.126 +#if DEBUG
1.127 + printf("not mapped!\n");
1.128 +#endif
1.129 +
1.130 + return -L4_ENOMEM;
1.131 +}
1.132 +
1.133 +/* Attach a region for provision when page faults occur. This is required in
1.134 + the initialisation of a program by the C library which requires a region
1.135 + mapper. */
1.136 +
1.137 +long InternalPager::attach(address_t *start, offset_t size, map_flags_t flags,
1.138 + l4_cap_idx_t ds, address_t offset,
1.139 + unsigned char align)
1.140 +{
1.141 +#if DEBUG
1.142 + printf("attach(%lx, %ld, %lx, ..., %lx, %d)\n", *start, size, flags, offset, align);
1.143 +#endif
1.144 +
1.145 + if (align < L4_PAGESHIFT)
1.146 + align = L4_PAGESHIFT;
1.147 +
1.148 + offset_t increment = 1UL << align;
1.149 + offset_t region_size = round(size, increment);
1.150 +
1.151 + /* Either attempt to find an address for the specified region, starting from
1.152 + any indicated address. */
1.153 +
1.154 + if (flags & L4RE_RM_F_SEARCH_ADDR)
1.155 + {
1.156 + address_t region_start = trunc(*start, increment);
1.157 + ExecRegions::iterator it = _regions.upper_bound(*start);
1.158 +
1.159 + if (!region_start)
1.160 + region_start += increment;
1.161 +
1.162 +#if DEBUG
1.163 + printf("-> search from %lx -> %lx...\n", *start, region_start);
1.164 +#endif
1.165 +
1.166 + /* Before last known region. */
1.167 +
1.168 + while (it != _regions.end())
1.169 + {
1.170 + ExecRegions::iterator next = it;
1.171 + ExecRegion &r = it->second;
1.172 + address_t start_limit;
1.173 + address_t end_limit = r.start;
1.174 +
1.175 + /* Consider any preceding region. If no such region exists, choose an
1.176 + address at the start of memory. */
1.177 +
1.178 + if (it == _regions.begin())
1.179 + start_limit = L4_PAGESIZE;
1.180 + else
1.181 + {
1.182 + it--;
1.183 + ExecRegion &pr = it->second;
1.184 + start_limit = pr.start + pr.size;
1.185 + it = next;
1.186 + }
1.187 +
1.188 + /* Test against the limits. */
1.189 +
1.190 + if (region_start < start_limit)
1.191 + region_start = round(start_limit, increment);
1.192 +
1.193 + /* Investigate subsequent regions if not enough space exists between the
1.194 + preceding region (or start of memory) and the current region. */
1.195 +
1.196 + if ((region_start + region_size) > end_limit)
1.197 + {
1.198 + it++;
1.199 + if (it == _regions.end())
1.200 + return -L4_ENOMEM;
1.201 + }
1.202 + else
1.203 + break;
1.204 + }
1.205 +
1.206 +#if DEBUG
1.207 + printf("-> added region for %lx size %ld (%d)\n", region_start, region_size, page_order(region_size));
1.208 +#endif
1.209 +
1.210 + ExecRegion r = (ExecRegion) {region_start, region_size, flags & L4RE_DS_F_RIGHTS_MASK, ds};
1.211 +
1.212 + add(r);
1.213 +
1.214 + *start = region_start;
1.215 + return L4_EOK;
1.216 + }
1.217 +
1.218 + /* Or attempt to add the specified region at a specific address. */
1.219 +
1.220 + else
1.221 + {
1.222 + // NOTE: To be implemented.
1.223 +
1.224 +#if DEBUG
1.225 + printf("-> region of size %ld (%d) not added!\n", region_size, page_order(region_size));
1.226 +#endif
1.227 +
1.228 + return -L4_ENOMEM;
1.229 + }
1.230 +}
1.231 +
1.232 +/* vim: tabstop=2 expandtab shiftwidth=2
1.233 +*/