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