1// vi:set ft=cpp: -*- Mode: C++ -*- 2/** 3 * \internal 4 * \file 5 * \brief Region mapper server template. 6 */ 7/* 8 * (c) 2014 Alexander Warg <alexander.warg@kernkonzept.com> 9 * 10 * This file is part of TUD:OS and distributed under the terms of the 11 * GNU General Public License 2. 12 * Please see the COPYING-GPL-2 file for details. 13 * 14 * As a special exception, you may use this file as part of a free software 15 * library without restriction. Specifically, if other files instantiate 16 * templates or use macros or inline functions from this file, or you compile 17 * this file and link it with other files to produce an executable, this 18 * file does not by itself cause the resulting executable to be covered by 19 * the GNU General Public License. This exception does not however 20 * invalidate any other reasons why the executable file might be covered by 21 * the GNU General Public License. 22 */ 23#pragma once 24 25#include <l4/sys/types.h> 26#include <l4/re/rm> 27#include <l4/re/util/region_mapping> 28 29namespace L4Re { namespace Util { 30 31template<typename DERIVED, typename Dbg> 32struct Rm_server 33{ 34private: 35 DERIVED *rm() { return static_cast<DERIVED*>(this); } 36 DERIVED const *rm() const { return static_cast<DERIVED const *>(this); } 37 38public: 39 40 /** 41 * Implementation of L4Re::Rm::_attach 42 */ 43 long op_attach(L4Re::Rm::Rights, l4_addr_t &_start, 44 unsigned long size, Rm::Flags flags, 45 L4::Ipc::Snd_fpage ds_cap, L4Re::Rm::Offset offs, 46 unsigned char align, l4_cap_idx_t client_cap_idx) 47 { 48 typename DERIVED::Dataspace ds; 49 50 if (!(flags & Rm::F::Reserved)) 51 { 52 if (long r = rm()->validate_ds(static_cast<DERIVED*>(this)->server_iface(), ds_cap, flags.region_flags(), &ds)) 53 return r; 54 } 55 56 size = l4_round_page(size); 57 l4_addr_t start = l4_trunc_page(_start); 58 59 if (size < L4_PAGESIZE) 60 return -L4_EINVAL; 61 62 Rm::Region_flags r_flags = flags.region_flags(); 63 Rm::Attach_flags a_flags = flags.attach_flags(); 64 65 start = l4_addr_t(rm()->attach((void*)start, size, 66 typename DERIVED::Region_handler(ds, client_cap_idx, offs, r_flags), 67 a_flags, align)); 68 69 if (start == L4_INVALID_ADDR) 70 return -L4_EADDRNOTAVAIL; 71 72 _start = start; 73 return L4_EOK; 74 } 75 76 /** 77 * Implementation of L4Re::Rm::free_area 78 */ 79 long op_free_area(L4Re::Rm::Rights, l4_addr_t start) 80 { 81 if (!rm()->detach_area(start)) 82 return -L4_ENOENT; 83 84 return L4_EOK; 85 } 86 87 /** 88 * Implementation of L4Re::Rm::_find 89 */ 90 long op_find(L4Re::Rm::Rights, l4_addr_t &addr, unsigned long &size, 91 L4Re::Rm::Flags &flags, L4Re::Rm::Offset &offset, 92 L4::Cap<L4Re::Dataspace> &m) 93 { 94 if (!DERIVED::Have_find) 95 return -L4_EPERM; 96 97 Rm::Flags flag_area { 0 }; 98 99 typename DERIVED::Node r = rm()->find(Region(addr, addr + size -1)); 100 if (!r) 101 { 102 r = rm()->area_find(Region(addr, addr + size - 1)); 103 if (!r) 104 return -L4_ENOENT; 105 flag_area = Rm::F::In_area; 106 } 107 108 addr = r->first.start(); 109 size = r->first.end() + 1 - addr; 110 111 flags = r->second.flags() | flag_area; 112 offset = r->second.offset(); 113 m = L4::Cap<L4Re::Dataspace>(DERIVED::find_res(r->second.memory())); 114 return L4_EOK; 115 } 116 117 /** 118 * Implementation of L4Re::Rm::_detach 119 */ 120 long op_detach(L4Re::Rm::Rights, l4_addr_t addr, 121 unsigned long size, unsigned flags, 122 l4_addr_t &start, l4_addr_t &rsize, 123 l4_cap_idx_t &mem_cap) 124 { 125 Region r; 126 typename DERIVED::Region_handler h; 127 int err = rm()->detach((void*)addr, size, flags, &r, &h); 128 if (err < 0) 129 return err; 130 131 if (r.invalid()) 132 return -L4_ENOENT; 133 134 start = r.start(); 135 rsize = r.size(); 136 mem_cap = h.client_cap_idx(); 137 return err; 138 } 139 140 /** 141 * Implementation of L4Re::Rm::_reserve_area 142 */ 143 long op_reserve_area(L4Re::Rm::Rights, l4_addr_t &start, unsigned long size, 144 L4Re::Rm::Flags flags, unsigned char align) 145 { 146 start = rm()->attach_area(start, size, flags, align); 147 if (start == L4_INVALID_ADDR) 148 return -L4_EADDRNOTAVAIL; 149 return L4_EOK; 150 } 151 152 /** 153 * Implementation of L4Re::Rm::get_regions 154 */ 155 long op_get_regions(L4Re::Rm::Rights, l4_addr_t addr, 156 L4::Ipc::Ret_array<L4Re::Rm::Region> regions) 157 { 158 typename DERIVED::Node r; 159 unsigned num = 0; 160 while ((r = rm()->lower_bound(Region(addr)))) 161 { 162 Rm::Region &x = regions.value[num]; 163 x.start = r->first.start(); 164 x.end = r->first.end(); 165 166 if (++num >= regions.max) 167 break; 168 169 if (x.end >= rm()->max_addr()) 170 break; 171 addr = x.end + 1; 172 } 173 return num; 174 } 175 176 /** 177 * Implementation of L4Re::Rm::get_areas 178 */ 179 long op_get_areas(L4Re::Rm::Rights, l4_addr_t addr, 180 L4::Ipc::Ret_array<L4Re::Rm::Area> areas) 181 { 182 typename DERIVED::Node r; 183 unsigned num = 0; 184 while ((r = rm()->lower_bound_area(Region(addr)))) 185 { 186 Rm::Area &x = areas.value[num]; 187 x.start = r->first.start(); 188 x.end = r->first.end(); 189 190 if (++num >= areas.max) 191 break; 192 193 if (x.end >= rm()->max_addr()) 194 break; 195 196 addr = x.end + 1; 197 } 198 return num; 199 } 200 201private: 202 static void pager_set_result(L4::Ipc::Opt<L4::Ipc::Snd_fpage> *fp, 203 L4::Ipc::Snd_fpage const &f) 204 { *fp = f; } 205 206 static void pager_set_result(L4::Ipc::Opt<L4::Ipc::Snd_fpage> *, ...) 207 {} 208public: 209 210 /** 211 * Pager API 212 */ 213 long op_io_page_fault(L4::Io_pager::Rights, l4_fpage_t, l4_umword_t, 214 L4::Ipc::Opt<L4::Ipc::Snd_fpage> &) 215 { 216 // generate exception 217 return -L4_ENOMEM; 218 } 219 220 long op_page_fault(L4::Pager::Rights, l4_umword_t addr, l4_umword_t pc, 221 L4::Ipc::Opt<L4::Ipc::Snd_fpage> &fp) 222 { 223 Dbg(Dbg::Server).printf("page fault: %lx pc=%lx\n", addr, pc); 224 225 bool need_w = addr & 2; 226 bool need_x = addr & 4; 227 228 typename DERIVED::Node n = rm()->find(addr); 229 230 if (!n || !n->second.memory()) 231 { 232 Dbg(Dbg::Warn, "rm").printf("unhandled %s page fault at 0x%lx pc=0x%lx\n", 233 need_w ? "write" : "read", addr, pc); 234 // generate exception 235 return -L4_ENOMEM; 236 } 237 238 if (!(n->second.flags() & L4Re::Rm::F::W) && need_w) 239 { 240 Dbg(Dbg::Warn, "rm").printf("write page fault in readonly region at 0x%lx pc=0x%lx\n", 241 addr, pc); 242 // generate exception 243 return -L4_EACCESS; 244 } 245 246 if (!(n->second.flags() & L4Re::Rm::F::X) && need_x) 247 { 248 Dbg(Dbg::Warn, "rm").printf("instruction page fault in non-exec region at 0x%lx pc=0x%lx\n", 249 addr, pc); 250 // generate exception 251 return -L4_EACCESS; 252 } 253 254 typename DERIVED::Region_handler::Ops::Map_result map_res; 255 if (int err = n->second.map(addr, n->first, need_w, &map_res)) 256 { 257 Dbg(Dbg::Warn, "rm").printf("mapping for page fault failed with error %d at 0x%lx pc=0x%lx\n", 258 err, addr, pc); 259 // generate exception 260 return -L4_ENOMEM; 261 } 262 263 pager_set_result(&fp, map_res); 264 return L4_EOK; 265 } 266}; 267 268}} 269