L4Re/departure

Annotated libexec/lib/src/external_pager.cc

551:e83cefef2cd7
19 months ago Paul Boddie Make the pagers loop forever when encountering an exception.
paul@366 1
/*
paul@366 2
 * A system pager implementation residing in a separate task.
paul@366 3
 *
paul@472 4
 * Copyright (C) 2022, 2023 Paul Boddie <paul@boddie.org.uk>
paul@366 5
 *
paul@366 6
 * This program is free software; you can redistribute it and/or
paul@366 7
 * modify it under the terms of the GNU General Public License as
paul@366 8
 * published by the Free Software Foundation; either version 2 of
paul@366 9
 * the License, or (at your option) any later version.
paul@366 10
 *
paul@366 11
 * This program is distributed in the hope that it will be useful,
paul@366 12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
paul@366 13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
paul@366 14
 * GNU General Public License for more details.
paul@366 15
 *
paul@366 16
 * You should have received a copy of the GNU General Public License
paul@366 17
 * along with this program; if not, write to the Free Software
paul@366 18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
paul@366 19
 * Boston, MA  02110-1301, USA
paul@366 20
 */
paul@366 21
paul@366 22
#include <l4/re/env.h>
paul@492 23
#include <l4/sys/task.h>
paul@366 24
#include <l4/util/util.h>
paul@366 25
paul@492 26
#include <ipc/cap_alloc.h>
paul@501 27
#include <ipc/map.h>
paul@366 28
#include <ipc/mem_ipc.h>
paul@366 29
#include <mem/memory_utils.h>
paul@472 30
#include <systypes/base.h>
paul@366 31
paul@366 32
#include <stdio.h>
paul@366 33
paul@366 34
#include "external_pager.h"
paul@489 35
#include "parent_pager_object_server.h"
paul@366 36
paul@366 37
paul@366 38
paul@431 39
#define DEBUG 0
paul@431 40
paul@431 41
paul@431 42
paul@366 43
/* A simple system pager also acting as a region mapper. */
paul@366 44
paul@431 45
ExternalPager::ExternalPager(address_t start, address_t end)
paul@543 46
: ExecPager(start, end)
paul@366 47
{
paul@366 48
}
paul@366 49
paul@489 50
ipc_server_default_config_type ExternalPager::config()
paul@489 51
{
paul@492 52
  return config_ParentPagerObject;
paul@492 53
}
paul@492 54
paul@492 55
paul@492 56
paul@492 57
/* Close the pager. */
paul@492 58
paul@492 59
void ExternalPager::close()
paul@492 60
{
paul@515 61
  printf("External pager closing...\n");
paul@492 62
paul@504 63
  /* Remove pager regions to avoid unmapping them twice. */
paul@504 64
paul@504 65
  remove(_rm_stack->region());
paul@504 66
paul@504 67
  for (unsigned int i = 0; i < _rm_payload->segments(); i++)
paul@504 68
    remove(_rm_payload->segment(i)->region());
paul@504 69
paul@504 70
  /* Delete the pager resources. */
paul@504 71
paul@504 72
  if (_rm_payload != NULL)
paul@504 73
    delete _rm_payload;
paul@504 74
paul@504 75
  if (_rm_stack != NULL)
paul@504 76
    delete _rm_stack;
paul@504 77
paul@504 78
  /* Unmap all remaining regions. */
paul@492 79
paul@515 80
  AvailableMemoryArea::iterator it;
paul@492 81
paul@515 82
  for (it = _area.begin(); it != _area.end(); it++)
paul@492 83
  {
paul@515 84
    MemoryArea *r = *it;
paul@515 85
paul@515 86
    if (r->is_mapped())
paul@515 87
      ipc_detach_dataspace((void *) r->dataspace_start());
paul@515 88
  }
paul@492 89
paul@515 90
  /* Free all capabilities. */
paul@515 91
paul@515 92
  Capabilities::iterator itc;
paul@515 93
paul@515 94
  for (itc = _dataspaces.begin(); itc != _dataspaces.end(); itc++)
paul@515 95
    ipc_cap_free_um(*itc);
paul@499 96
paul@543 97
  /* Notify the monitor. */
paul@501 98
paul@543 99
  if (_monitor != NULL)
paul@543 100
    _monitor->pager_ended();
paul@489 101
}
paul@489 102
paul@492 103
paul@492 104
paul@543 105
/* Capability management. */
paul@492 106
paul@503 107
void ExternalPager::set_pager(l4_cap_idx_t cap, l4_cap_idx_t mapped_cap)
paul@499 108
{
paul@499 109
  _pager = cap;
paul@503 110
  _mapped_pager = mapped_cap;
paul@499 111
}
paul@499 112
paul@503 113
void ExternalPager::set_parent(l4_cap_idx_t cap, l4_cap_idx_t mapped_cap)
paul@499 114
{
paul@499 115
  _parent = cap;
paul@503 116
  _mapped_parent = mapped_cap;
paul@499 117
}
paul@499 118
paul@503 119
void ExternalPager::set_task(l4_cap_idx_t cap, l4_cap_idx_t mapped_cap)
paul@499 120
{
paul@499 121
  _task = cap;
paul@503 122
  _mapped_task = mapped_cap;
paul@499 123
}
paul@499 124
paul@543 125
void ExternalPager::set_thread(l4_cap_idx_t cap, l4_cap_idx_t mapped_cap)
paul@543 126
{
paul@543 127
  _thread = cap;
paul@543 128
  _mapped_thread = mapped_cap;
paul@543 129
}
paul@543 130
paul@543 131
paul@543 132
paul@543 133
/* Lifecycle management. */
paul@543 134
paul@543 135
void ExternalPager::set_monitor(ProcessMonitor *monitor)
paul@543 136
{
paul@543 137
  _monitor = monitor;
paul@543 138
}
paul@543 139
paul@492 140
paul@492 141
paul@504 142
/* Manage pager resources. */
paul@504 143
paul@504 144
void ExternalPager::set_payload(Payload *payload)
paul@504 145
{
paul@504 146
  _rm_payload = payload;
paul@504 147
}
paul@504 148
paul@504 149
void ExternalPager::set_stack(ExplicitSegment *stack)
paul@504 150
{
paul@504 151
  _rm_stack = stack;
paul@504 152
}
paul@504 153
paul@504 154
paul@504 155
paul@366 156
/* Handle a general exception. */
paul@366 157
paul@366 158
long ExternalPager::exception(l4_exc_regs_t regs, l4_snd_fpage_t *region)
paul@366 159
{
paul@366 160
  (void) region;
paul@366 161
paul@431 162
  printf("ExternalPager::exception(...) -> pfa = %lx, pc = %lx\n", l4_utcb_exc_pfa(&regs), l4_utcb_exc_pc(&regs));
paul@366 163
paul@376 164
  while (1)
paul@376 165
    l4_sleep_forever();
paul@376 166
paul@366 167
  return L4_EOK;
paul@366 168
}
paul@366 169
paul@366 170
/* Handle a page fault using any configured regions. */
paul@366 171
paul@366 172
long ExternalPager::page_fault(l4_umword_t pfa, l4_umword_t pc, l4_snd_fpage_t *region)
paul@366 173
{
paul@366 174
  l4_umword_t addr = pfa & ~7UL, flags = pfa & 7;
paul@366 175
paul@366 176
#if DEBUG
paul@366 177
  printf("page_fault(%lx, %lx) -> %lx (%lx) -> ", pfa, pc, addr, flags);
paul@366 178
#endif
paul@366 179
paul@515 180
  /* Obtain a region supporting the fault address. */
paul@366 181
paul@545 182
  MemoryArea *r, *parent;
paul@545 183
  long err = _area.find(addr, &r, &parent);
paul@366 184
paul@515 185
  if (!err)
paul@366 186
  {
paul@366 187
    l4_addr_t page_addr = trunc(addr, L4_PAGESIZE);
paul@472 188
    map_flags_t map_flags = map_flags_for_fault(flags);
paul@376 189
paul@515 190
    region->fpage = l4_fpage(r->dataspace_start() + (page_addr - r->area_start()), L4_PAGESHIFT, map_flags & r->flags());
paul@366 191
    region->snd_base = page_addr;
paul@366 192
paul@366 193
#if DEBUG
paul@376 194
    printf("%lx...%lx from %lx...%lx offset %lx size %d rights %x ds %lx\n",
paul@515 195
           r->area_start(), region->snd_base,
paul@515 196
           r->dataspace_start(), l4_fpage_memaddr(region->fpage),
paul@515 197
           addr - r->area_start(),
paul@366 198
           l4_fpage_size(region->fpage),
paul@376 199
           l4_fpage_rights(region->fpage),
paul@515 200
           r->dataspace());
paul@366 201
paul@366 202
    printf("%lx -> ", addr);
paul@366 203
paul@366 204
    for (unsigned int i = 0; i < sizeof(l4_umword_t); i++)
paul@515 205
      printf("%02x", *((unsigned char *)(r->dataspace_start() + (addr - r->area_start()) + i)));
paul@366 206
paul@366 207
    printf("\n");
paul@366 208
#endif
paul@366 209
paul@515 210
    if (r->flags() & L4RE_RM_F_W)
paul@515 211
      l4_touch_rw((const void *) (r->dataspace_start() + (page_addr - r->area_start())), L4_PAGESIZE);
paul@366 212
    else
paul@515 213
      l4_touch_ro((const void *) (r->dataspace_start() + (page_addr - r->area_start())), L4_PAGESIZE);
paul@366 214
paul@366 215
    return L4_EOK;
paul@366 216
  }
paul@366 217
paul@515 218
  printf("not mapped at %lx for pc %lx\n", addr, pc);
paul@366 219
paul@515 220
  return err;
paul@366 221
}
paul@366 222
paul@366 223
/* Attach a region for provision when page faults occur. This is required in
paul@366 224
   the initialisation of a program by the C library which requires a region
paul@366 225
   mapper. */
paul@366 226
paul@419 227
long ExternalPager::attach(address_t *start, address_t size, map_flags_t flags,
paul@366 228
                           l4_cap_idx_t ds, address_t offset,
paul@366 229
                           unsigned char align)
paul@366 230
{
paul@515 231
  // NOTE: Determine the purpose of offset.
paul@515 232
paul@515 233
  (void) offset;
paul@515 234
paul@515 235
  MemoryArea *area;
paul@515 236
  long err = ExecPager::find(start, &size, flags, align, &area);
paul@366 237
paul@499 238
  /* Without an error, attach the dataspace. */
paul@499 239
paul@431 240
  if (!err)
paul@431 241
  {
paul@499 242
    /* Attach the provided dataspace within this task.
paul@472 243
paul@472 244
       This is only done in this implementation to support the paging
paul@472 245
       mechanism. In a region mapper residing within the actual task, the
paul@472 246
       dataspace's map operation would be invoked to obtain mappings. */
paul@366 247
paul@366 248
    l4_addr_t ds_start;
paul@431 249
    err = ipc_attach_dataspace(ds, size, (void **) &ds_start);
paul@366 250
paul@366 251
    if (err)
paul@366 252
      return err;
paul@366 253
paul@366 254
    l4_touch_rw((const void *) ds_start, size);
paul@366 255
paul@515 256
    MappedRegion r(*start, *start + size, flags & L4RE_DS_F_RIGHTS_MASK, ds, ds_start);
paul@515 257
    area->add(r);
paul@515 258
paul@515 259
    /* Record dataspaces separately. */
paul@515 260
paul@515 261
    _dataspaces.insert(ds);
paul@366 262
  }
paul@366 263
paul@499 264
  /* Discard the imported dataspace if its region cannot be accommodated. */
paul@499 265
paul@499 266
  else
paul@499 267
    ipc_cap_free_um(ds);
paul@499 268
paul@431 269
  return err;
paul@366 270
}
paul@366 271
paul@492 272
paul@492 273
paul@469 274
/* Receive signals from a task. */
paul@469 275
paul@469 276
long ExternalPager::signal(unsigned long sig, unsigned long val)
paul@469 277
{
paul@543 278
  /* Handle the termination event of the internal pager. */
paul@492 279
paul@492 280
  if (sig == 0)
paul@492 281
  {
paul@543 282
    printf("Signal from internal pager.\n");
paul@492 283
paul@543 284
    /* For some reason, threads cannot be released by the process, so they are
paul@543 285
       also unmapped on its behalf. */
paul@492 286
paul@543 287
    if (l4_is_valid_cap(_thread))
paul@492 288
    {
paul@543 289
      ipc_cap_free_um(_thread);
paul@543 290
      ipc_unmap_capability(_task, _mapped_thread);
paul@543 291
      _thread = L4_INVALID_CAP;
paul@499 292
paul@543 293
      /* Parent and pager/region mapper. Freeing the pager after the thread
paul@543 294
         should avoid warnings about invalid pager capabilities. */
paul@528 295
paul@528 296
      ipc_unmap_capability(_task, _mapped_parent);
paul@528 297
      ipc_unmap_capability(_task, _mapped_pager);
paul@492 298
    }
paul@492 299
  }
paul@492 300
paul@469 301
  return L4_EOK;
paul@469 302
}
paul@469 303
paul@366 304
/* vim: tabstop=2 expandtab shiftwidth=2
paul@366 305
*/