// vi:set ft=cpp: -*- Mode: C++ -*- /** * \internal * \file * \brief Region mapper server template. */ /* * (c) 2014 Alexander Warg * * This file is part of TUD:OS and distributed under the terms of the * GNU General Public License 2. * Please see the COPYING-GPL-2 file for details. * * As a special exception, you may use this file as part of a free software * library without restriction. Specifically, if other files instantiate * templates or use macros or inline functions from this file, or you compile * this file and link it with other files to produce an executable, this * file does not by itself cause the resulting executable to be covered by * the GNU General Public License. This exception does not however * invalidate any other reasons why the executable file might be covered by * the GNU General Public License. */ #pragma once #include #include #include namespace L4Re { namespace Util { template struct Rm_server { private: DERIVED *rm() { return static_cast(this); } DERIVED const *rm() const { return static_cast(this); } public: /** * Implementation of L4Re::Rm::_attach */ long op_attach(L4Re::Rm::Rights, l4_addr_t &_start, unsigned long size, Rm::Flags flags, L4::Ipc::Snd_fpage ds_cap, L4Re::Rm::Offset offs, unsigned char align, l4_cap_idx_t client_cap_idx) { typename DERIVED::Dataspace ds; if (!(flags & Rm::F::Reserved)) { if (long r = rm()->validate_ds(static_cast(this)->server_iface(), ds_cap, flags.region_flags(), &ds)) return r; } size = l4_round_page(size); l4_addr_t start = l4_trunc_page(_start); if (size < L4_PAGESIZE) return -L4_EINVAL; Rm::Region_flags r_flags = flags.region_flags(); Rm::Attach_flags a_flags = flags.attach_flags(); start = l4_addr_t(rm()->attach((void*)start, size, typename DERIVED::Region_handler(ds, client_cap_idx, offs, r_flags), a_flags, align)); if (start == L4_INVALID_ADDR) return -L4_EADDRNOTAVAIL; _start = start; return L4_EOK; } /** * Implementation of L4Re::Rm::free_area */ long op_free_area(L4Re::Rm::Rights, l4_addr_t start) { if (!rm()->detach_area(start)) return -L4_ENOENT; return L4_EOK; } /** * Implementation of L4Re::Rm::_find */ long op_find(L4Re::Rm::Rights, l4_addr_t &addr, unsigned long &size, L4Re::Rm::Flags &flags, L4Re::Rm::Offset &offset, L4::Cap &m) { if (!DERIVED::Have_find) return -L4_EPERM; Rm::Flags flag_area { 0 }; typename DERIVED::Node r = rm()->find(Region(addr, addr + size -1)); if (!r) { r = rm()->area_find(Region(addr, addr + size - 1)); if (!r) return -L4_ENOENT; flag_area = Rm::F::In_area; } addr = r->first.start(); size = r->first.end() + 1 - addr; flags = r->second.flags() | flag_area; offset = r->second.offset(); m = L4::Cap(DERIVED::find_res(r->second.memory())); return L4_EOK; } /** * Implementation of L4Re::Rm::_detach */ long op_detach(L4Re::Rm::Rights, l4_addr_t addr, unsigned long size, unsigned flags, l4_addr_t &start, l4_addr_t &rsize, l4_cap_idx_t &mem_cap) { Region r; typename DERIVED::Region_handler h; int err = rm()->detach((void*)addr, size, flags, &r, &h); if (err < 0) return err; if (r.invalid()) return -L4_ENOENT; start = r.start(); rsize = r.size(); mem_cap = h.client_cap_idx(); return err; } /** * Implementation of L4Re::Rm::_reserve_area */ long op_reserve_area(L4Re::Rm::Rights, l4_addr_t &start, unsigned long size, L4Re::Rm::Flags flags, unsigned char align) { start = rm()->attach_area(start, size, flags, align); if (start == L4_INVALID_ADDR) return -L4_EADDRNOTAVAIL; return L4_EOK; } /** * Implementation of L4Re::Rm::get_regions */ long op_get_regions(L4Re::Rm::Rights, l4_addr_t addr, L4::Ipc::Ret_array regions) { typename DERIVED::Node r; unsigned num = 0; while ((r = rm()->lower_bound(Region(addr)))) { Rm::Region &x = regions.value[num]; x.start = r->first.start(); x.end = r->first.end(); if (++num >= regions.max) break; if (x.end >= rm()->max_addr()) break; addr = x.end + 1; } return num; } /** * Implementation of L4Re::Rm::get_areas */ long op_get_areas(L4Re::Rm::Rights, l4_addr_t addr, L4::Ipc::Ret_array areas) { typename DERIVED::Node r; unsigned num = 0; while ((r = rm()->lower_bound_area(Region(addr)))) { Rm::Area &x = areas.value[num]; x.start = r->first.start(); x.end = r->first.end(); if (++num >= areas.max) break; if (x.end >= rm()->max_addr()) break; addr = x.end + 1; } return num; } private: static void pager_set_result(L4::Ipc::Opt *fp, L4::Ipc::Snd_fpage const &f) { *fp = f; } static void pager_set_result(L4::Ipc::Opt *, ...) {} public: /** * Pager API */ long op_io_page_fault(L4::Io_pager::Rights, l4_fpage_t, l4_umword_t, L4::Ipc::Opt &) { // generate exception return -L4_ENOMEM; } long op_page_fault(L4::Pager::Rights, l4_umword_t addr, l4_umword_t pc, L4::Ipc::Opt &fp) { Dbg(Dbg::Server).printf("page fault: %lx pc=%lx\n", addr, pc); bool need_w = addr & 2; bool need_x = addr & 4; typename DERIVED::Node n = rm()->find(addr); if (!n || !n->second.memory()) { Dbg(Dbg::Warn, "rm").printf("unhandled %s page fault at 0x%lx pc=0x%lx\n", need_w ? "write" : "read", addr, pc); // generate exception return -L4_ENOMEM; } if (!(n->second.flags() & L4Re::Rm::F::W) && need_w) { Dbg(Dbg::Warn, "rm").printf("write page fault in readonly region at 0x%lx pc=0x%lx\n", addr, pc); // generate exception return -L4_EACCESS; } if (!(n->second.flags() & L4Re::Rm::F::X) && need_x) { Dbg(Dbg::Warn, "rm").printf("instruction page fault in non-exec region at 0x%lx pc=0x%lx\n", addr, pc); // generate exception return -L4_EACCESS; } typename DERIVED::Region_handler::Ops::Map_result map_res; if (int err = n->second.map(addr, n->first, need_w, &map_res)) { Dbg(Dbg::Warn, "rm").printf("mapping for page fault failed with error %d at 0x%lx pc=0x%lx\n", err, addr, pc); // generate exception return -L4_ENOMEM; } pager_set_result(&fp, map_res); return L4_EOK; } }; }}