L4Re/departure

Annotated tests/dstest_exec.cc

320:a206608cd294
2022-05-05 Paul Boddie Renamed Region to MappedRegion, located the stack below the UTCB area.
paul@308 1
/*
paul@308 2
 * Support for executing code in new tasks and threads.
paul@308 3
 *
paul@308 4
 * Copyright (C) 2022 Paul Boddie <paul@boddie.org.uk>
paul@308 5
 *
paul@308 6
 * This program is free software; you can redistribute it and/or
paul@308 7
 * modify it under the terms of the GNU General Public License as
paul@308 8
 * published by the Free Software Foundation; either version 2 of
paul@308 9
 * the License, or (at your option) any later version.
paul@308 10
 *
paul@308 11
 * This program is distributed in the hope that it will be useful,
paul@308 12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
paul@308 13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
paul@308 14
 * GNU General Public License for more details.
paul@308 15
 *
paul@308 16
 * You should have received a copy of the GNU General Public License
paul@308 17
 * along with this program; if not, write to the Free Software
paul@308 18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
paul@308 19
 * Boston, MA  02110-1301, USA
paul@308 20
 */
paul@308 21
paul@308 22
#include <l4/re/env.h>
paul@308 23
#include <l4/sys/err.h>
paul@308 24
#include <l4/sys/factory.h>
paul@308 25
#include <l4/sys/task.h>
paul@308 26
#include <l4/sys/thread.h>
paul@312 27
#include <l4/util/elf.h>
paul@312 28
#include <l4/util/util.h>
paul@308 29
paul@315 30
#include <l4/libloader/adjust_stack>
paul@315 31
paul@308 32
#include <fsclient/client.h>
paul@308 33
#include <ipc/cap_alloc.h>
paul@308 34
#include <ipc/mem_ipc.h>
paul@308 35
#include <ipc/server.h>
paul@311 36
#include <mem/memory_utils.h>
paul@308 37
#include <systypes/fcntl.h>
paul@308 38
paul@312 39
#include <map>
paul@312 40
paul@308 41
#include <stdio.h>
paul@308 42
#include <stdlib.h>
paul@308 43
#include <string.h>
paul@308 44
paul@308 45
#include <pthread-l4.h>
paul@308 46
#include <pthread.h>
paul@308 47
paul@312 48
#include "pager_object_interface.h"
paul@312 49
#include "pager_object_server.h"
paul@308 50
paul@308 51
paul@308 52
paul@319 53
/* UTCB properties.
paul@319 54
   See: moe/server/src/loader.cc */
paul@319 55
paul@319 56
enum ipc_exec_utcb
paul@319 57
{
paul@319 58
  Default_max_threads = 16,
paul@319 59
#ifdef ARCH_mips
paul@319 60
  Utcb_area_start = 0x73000000,
paul@319 61
#else
paul@319 62
  Utcb_area_start = 0xb3000000,
paul@319 63
#endif
paul@319 64
};
paul@319 65
paul@319 66
static l4_fpage_t get_utcb_fpage()
paul@319 67
{
paul@319 68
  /* UTCB location and size. */
paul@319 69
paul@319 70
  int utcb_log2size = page_order(Default_max_threads * L4_UTCB_OFFSET);
paul@319 71
paul@319 72
  /* Round up to at least one page. */
paul@319 73
paul@319 74
  if (utcb_log2size < L4_PAGESHIFT)
paul@319 75
    utcb_log2size = L4_PAGESHIFT;
paul@319 76
paul@320 77
  return l4_fpage(Utcb_area_start, utcb_log2size, 0);
paul@319 78
}
paul@319 79
paul@319 80
paul@319 81
paul@320 82
/* Mapped region data structures. */
paul@308 83
paul@320 84
class MappedRegion
paul@308 85
{
paul@308 86
public:
paul@312 87
  l4_addr_t start;
paul@312 88
  unsigned int log2size;
paul@312 89
  l4_umword_t flags;
paul@312 90
  l4_addr_t map_start;
paul@312 91
paul@320 92
  explicit MappedRegion()
paul@312 93
  : start(0), log2size(0), flags(0), map_start(0)
paul@312 94
  {
paul@312 95
  }
paul@312 96
paul@320 97
  explicit MappedRegion(l4_addr_t start, unsigned int log2size,
paul@320 98
                        l4_umword_t flags, l4_addr_t map_start)
paul@312 99
  : start(start), log2size(log2size), flags(flags), map_start(map_start)
paul@312 100
  {
paul@312 101
  }
paul@312 102
};
paul@312 103
paul@316 104
paul@316 105
paul@316 106
/* Program segment abstraction. */
paul@316 107
paul@316 108
class Segment
paul@316 109
{
paul@316 110
protected:
paul@320 111
  MappedRegion _region;
paul@316 112
paul@316 113
public:
paul@316 114
  /* Allocated memory. */
paul@316 115
paul@316 116
  char *buf;
paul@316 117
  l4re_ds_t ds;
paul@316 118
paul@316 119
  /* Segment base and corresponding region base. */
paul@316 120
paul@316 121
  l4_addr_t base, region_base;
paul@316 122
paul@316 123
  /* Segment size and corresponding region size. */
paul@316 124
paul@316 125
  offset_t size, region_size;
paul@316 126
paul@316 127
  /* Offset of segment content within the region. */
paul@316 128
paul@316 129
  offset_t region_offset;
paul@316 130
paul@316 131
  /* Access flags. */
paul@316 132
paul@316 133
  l4re_rm_flags_t flags;
paul@316 134
paul@316 135
  /* File access details. */
paul@316 136
paul@316 137
  offset_t file_offset, file_contents;
paul@316 138
paul@316 139
  explicit Segment(offset_t base, offset_t size, l4re_rm_flags_t flags,
paul@316 140
                   offset_t file_offset = 0, offset_t file_contents = 0)
paul@316 141
  : base(base), size(size), flags(flags), file_offset(file_offset), file_contents(file_contents)
paul@316 142
  {
paul@316 143
    region_base = trunc(base, L4_PAGESIZE);
paul@316 144
    region_offset = base - region_base;
paul@316 145
    region_size = round(size, L4_PAGESIZE);
paul@316 146
  }
paul@316 147
paul@316 148
  long allocate();
paul@316 149
paul@316 150
  long fill(file_t *file);
paul@316 151
paul@320 152
  MappedRegion &region();
paul@316 153
paul@316 154
  l4_addr_t region_address(char *address);
paul@316 155
paul@316 156
  l4_addr_t region_address(l4_addr_t address);
paul@316 157
};
paul@316 158
paul@316 159
long Segment::allocate()
paul@316 160
{
paul@316 161
  return ipc_allocate_align(size, L4RE_RM_F_SEARCH_ADDR | flags,
paul@316 162
                           L4_PAGESHIFT, (void **) &buf, &ds);
paul@316 163
}
paul@316 164
paul@316 165
long Segment::fill(file_t *file)
paul@316 166
{
paul@316 167
  if (!file_contents)
paul@316 168
    return L4_EOK;
paul@316 169
paul@316 170
  memset(buf, 0, region_size);
paul@316 171
  client_seek(file, file_offset, SEEK_SET);
paul@316 172
  offset_t nread = client_read(file, buf + region_offset, file_contents);
paul@316 173
paul@316 174
  if (nread < file_contents)
paul@316 175
    return -L4_EIO;
paul@316 176
  else
paul@316 177
    return L4_EOK;
paul@316 178
}
paul@316 179
paul@320 180
MappedRegion &Segment::region()
paul@316 181
{
paul@320 182
  _region = MappedRegion((l4_addr_t) buf, page_order(region_size), flags, region_base);
paul@316 183
  return _region;
paul@316 184
}
paul@316 185
paul@316 186
l4_addr_t Segment::region_address(char *address)
paul@316 187
{
paul@316 188
  return (l4_addr_t) ((address - buf) + (char *) region_base);
paul@316 189
}
paul@316 190
paul@316 191
l4_addr_t Segment::region_address(l4_addr_t address)
paul@316 192
{
paul@316 193
  return (address - (l4_addr_t) buf) + region_base;
paul@316 194
}
paul@312 195
paul@312 196
paul@312 197
paul@312 198
/* A simple system pager also acting as a region mapper. */
paul@312 199
paul@320 200
typedef std::map<l4_addr_t, MappedRegion> MappedRegions;
paul@316 201
paul@312 202
class ExecPager : public PagerObject
paul@312 203
{
paul@312 204
protected:
paul@320 205
  MappedRegions _regions;
paul@312 206
paul@312 207
public:
paul@320 208
  virtual void add(MappedRegion region)
paul@312 209
  {
paul@312 210
    _regions[region.map_start] = region;
paul@312 211
  }
paul@312 212
paul@312 213
  /* Notification methods. */
paul@308 214
paul@308 215
  virtual long exception(l4_exc_regs_t regs,
paul@308 216
                         l4_snd_fpage_t *region);
paul@308 217
paul@308 218
  virtual long page_fault(l4_umword_t pfa, l4_umword_t pc,
paul@308 219
                          l4_snd_fpage_t *region);
paul@312 220
paul@312 221
  /* Region manager/mapper methods. */
paul@312 222
paul@312 223
  virtual long attach(address_t *start, offset_t size, map_flags_t flags,
paul@312 224
                      l4_cap_idx_t ds, address_t offset, unsigned char align);
paul@312 225
paul@308 226
};
paul@308 227
paul@312 228
/* Handle a general exception. */
paul@312 229
paul@308 230
long ExecPager::exception(l4_exc_regs_t regs, l4_snd_fpage_t *region)
paul@308 231
{
paul@308 232
  (void) region;
paul@308 233
paul@308 234
  printf("exception(...) -> pfa = %lx, pc = %lx\n", l4_utcb_exc_pfa(&regs), l4_utcb_exc_pc(&regs));
paul@312 235
paul@312 236
  printf("r15 = %lx\n", regs.r15);
paul@312 237
  printf("r14 = %lx\n", regs.r14);
paul@312 238
  printf("r13 = %lx\n", regs.r13);
paul@312 239
  printf("r12 = %lx\n", regs.r12);
paul@312 240
  printf("r11 = %lx\n", regs.r11);
paul@312 241
  printf("r10 = %lx\n", regs.r10);
paul@312 242
  printf("r9 = %lx\n", regs.r9);
paul@312 243
  printf("r8 = %lx\n", regs.r8);
paul@312 244
  printf("rdi = %lx\n", regs.rdi);
paul@312 245
  printf("rsi = %lx\n", regs.rsi);
paul@312 246
  printf("rbp = %lx\n", regs.rbp);
paul@312 247
  printf("pfa = %lx\n", regs.pfa);
paul@312 248
  printf("rbx = %lx\n", regs.rbx);
paul@312 249
  printf("rdx = %lx\n", regs.rdx);
paul@312 250
  printf("rcx = %lx\n", regs.rcx);
paul@312 251
  printf("rax = %lx\n", regs.rax);
paul@312 252
  printf("trapno = %lx\n", regs.trapno);
paul@312 253
  printf("err = %lx\n", regs.err);
paul@312 254
  printf("ip = %lx\n", regs.ip);
paul@312 255
  printf("flags = %lx\n", regs.flags);
paul@312 256
  printf("sp = %lx\n", regs.sp);
paul@312 257
  printf("ss = %lx\n", regs.ss);
paul@312 258
  printf("fs_base = %lx\n", regs.fs_base);
paul@312 259
  printf("gs_base = %lx\n", regs.gs_base);
paul@312 260
paul@308 261
  return L4_EOK;
paul@308 262
}
paul@308 263
paul@315 264
#define DEBUG 0
paul@315 265
paul@312 266
/* Handle a page fault using any configured regions. */
paul@312 267
paul@308 268
long ExecPager::page_fault(l4_umword_t pfa, l4_umword_t pc, l4_snd_fpage_t *region)
paul@308 269
{
paul@308 270
  l4_umword_t addr = pfa & ~7UL, flags = pfa & 7;
paul@308 271
paul@315 272
#if DEBUG
paul@312 273
  printf("page_fault(%lx, %lx) -> %lx (%lx) -> ", pfa, pc, addr, flags);
paul@312 274
#endif
paul@312 275
paul@320 276
  MappedRegions::iterator it = _regions.upper_bound(addr);
paul@312 277
paul@312 278
  if (it != _regions.begin())
paul@312 279
    it--;
paul@312 280
  else
paul@312 281
  {
paul@312 282
    printf("not mapped!\n");
paul@312 283
    return -L4_ENOMEM;
paul@312 284
  }
paul@312 285
paul@320 286
  MappedRegion &r = it->second;
paul@308 287
paul@312 288
  if ((addr >= r.map_start) && (addr < r.map_start + (1UL << r.log2size)))
paul@308 289
  {
paul@312 290
    l4_addr_t page_addr = trunc(addr, L4_PAGESIZE);
paul@312 291
paul@312 292
    region->fpage = l4_fpage(r.start + (page_addr - r.map_start), L4_PAGESHIFT, r.flags);
paul@312 293
    region->snd_base = page_addr;
paul@312 294
paul@315 295
#if DEBUG
paul@312 296
    printf("%lx...%lx from %lx...%lx size %d rights %x\n",
paul@312 297
           r.map_start, region->snd_base,
paul@312 298
           r.start, l4_fpage_memaddr(region->fpage),
paul@312 299
           l4_fpage_size(region->fpage),
paul@312 300
           l4_fpage_rights(region->fpage));
paul@312 301
    printf("%lx -> ", addr);
paul@312 302
paul@312 303
    for (unsigned int i = 0; i < sizeof(l4_umword_t); i++)
paul@312 304
      printf("%02x", *((unsigned char *)(r.start + (addr - r.map_start) + i)));
paul@312 305
paul@312 306
    printf("\n");
paul@312 307
#endif
paul@308 308
paul@308 309
    return L4_EOK;
paul@308 310
  }
paul@308 311
paul@315 312
#if DEBUG
paul@312 313
  printf("not mapped!\n");
paul@312 314
#endif
paul@312 315
paul@308 316
  return -L4_ENOMEM;
paul@308 317
}
paul@308 318
paul@312 319
/* Attach a region for provision when page faults occur. This is required in
paul@312 320
   the initialisation of a program by the C library which requires a region
paul@312 321
   mapper. */
paul@312 322
paul@312 323
long ExecPager::attach(address_t *start, offset_t size, map_flags_t flags,
paul@312 324
                       l4_cap_idx_t ds, address_t offset, unsigned char align)
paul@312 325
{
paul@315 326
#if DEBUG
paul@312 327
  printf("attach(%lx, %ld, %lx, ..., %lx, %d)\n", *start, size, flags, offset, align);
paul@312 328
#endif
paul@312 329
paul@312 330
  if (align < L4_PAGESHIFT)
paul@312 331
    align = L4_PAGESHIFT;
paul@312 332
paul@312 333
  offset_t increment = 1UL << align;
paul@312 334
  offset_t region_size = round(size, increment);
paul@312 335
paul@312 336
  /* Either attempt to find an address for the specified region, starting from
paul@312 337
     any indicated address. */
paul@312 338
paul@312 339
  if (flags & L4RE_RM_F_SEARCH_ADDR)
paul@312 340
  {
paul@312 341
    address_t region_start = trunc(*start, increment);
paul@320 342
    MappedRegions::iterator it = _regions.upper_bound(*start);
paul@312 343
paul@312 344
    if (!region_start)
paul@312 345
      region_start += increment;
paul@312 346
paul@315 347
#if DEBUG
paul@312 348
    printf("-> search from %lx -> %lx...\n", *start, region_start);
paul@312 349
#endif
paul@312 350
paul@312 351
    /* Before last known region. */
paul@312 352
paul@312 353
    while (it != _regions.end())
paul@312 354
    {
paul@320 355
      MappedRegions::iterator next = it;
paul@320 356
      MappedRegion &r = it->second;
paul@312 357
      address_t start_limit;
paul@312 358
      address_t end_limit = r.map_start;
paul@312 359
paul@312 360
      /* Consider any preceding region. If no such region exists, choose an
paul@312 361
         address at the start of memory. */
paul@312 362
paul@312 363
      if (it == _regions.begin())
paul@312 364
        start_limit = L4_PAGESIZE;
paul@312 365
      else
paul@312 366
      {
paul@312 367
        it--;
paul@320 368
        MappedRegion &pr = it->second;
paul@312 369
        start_limit = pr.map_start + (1UL << pr.log2size);
paul@312 370
        it = next;
paul@312 371
      }
paul@312 372
paul@312 373
      /* Test against the limits. */
paul@312 374
paul@312 375
      if (region_start < start_limit)
paul@312 376
        region_start = round(start_limit, increment);
paul@312 377
paul@312 378
      /* Investigate subsequent regions if not enough space exists between the
paul@312 379
         preceding region (or start of memory) and the current region. */
paul@312 380
paul@312 381
      if ((region_start + region_size) > end_limit)
paul@312 382
      {
paul@312 383
        it++;
paul@312 384
        if (it == _regions.end())
paul@312 385
          return -L4_ENOMEM;
paul@312 386
      }
paul@312 387
      else
paul@312 388
        break;
paul@312 389
    }
paul@312 390
paul@312 391
    /* Attach the provided dataspace.
paul@312 392
       NOTE: This is only done in this implementation to support the paging
paul@312 393
             mechanism. In a region mapper residing within the actual task, the
paul@312 394
             dataspace's map operation would be invoked to obtain mappings. */
paul@312 395
paul@312 396
    l4_addr_t ds_start;
paul@312 397
paul@312 398
    long err = ipc_attach_dataspace(ds, size, (void **) &ds_start);
paul@312 399
paul@312 400
    if (err)
paul@312 401
      return err;
paul@312 402
paul@312 403
    l4_touch_rw((const void *) ds_start, size);
paul@312 404
paul@315 405
#if DEBUG
paul@312 406
    printf("-> added region for %lx size %ld (%d)\n", region_start, region_size, page_order(region_size));
paul@312 407
#endif
paul@312 408
paul@320 409
    add(MappedRegion(ds_start, page_order(region_size), flags & L4RE_DS_F_RIGHTS_MASK, region_start));
paul@312 410
paul@312 411
    *start = region_start;
paul@312 412
    return L4_EOK;
paul@312 413
  }
paul@312 414
paul@312 415
  /* Or attempt to add the specified region at a specific address. */
paul@312 416
paul@312 417
  else
paul@312 418
  {
paul@312 419
    // NOTE: To be implemented.
paul@312 420
paul@312 421
    return -L4_ENOMEM;
paul@312 422
  }
paul@312 423
}
paul@312 424
paul@308 425
paul@308 426
paul@319 427
/* Capability mapping definitions for the new task. */
paul@317 428
paul@319 429
struct mapped_cap
paul@319 430
{
paul@319 431
  l4_umword_t index;
paul@319 432
  l4_cap_idx_t cap;
paul@319 433
  unsigned char rights;
paul@319 434
};
paul@317 435
paul@319 436
static long map_capabilities(l4_cap_idx_t task, struct mapped_cap mapped_caps[])
paul@319 437
{
paul@319 438
  long err = L4_EOK;
paul@319 439
  int i = 0;
paul@317 440
paul@319 441
  while (l4_is_valid_cap(mapped_caps[i].cap) && !err)
paul@318 442
  {
paul@319 443
    err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP,
paul@319 444
                               l4_obj_fpage(mapped_caps[i].cap, 0, mapped_caps[i].rights),
paul@319 445
                               l4_map_obj_control(mapped_caps[i].index, L4_MAP_ITEM_MAP)));
paul@319 446
    i++;
paul@318 447
  }
paul@317 448
paul@319 449
  return err;
paul@319 450
}
paul@319 451
paul@319 452
paul@319 453
paul@319 454
/* A stack abstraction. */
paul@319 455
paul@319 456
class Stack
paul@319 457
{
paul@319 458
  struct auxv_entry
paul@319 459
  {
paul@319 460
    l4_umword_t key, value;
paul@319 461
  };
paul@319 462
paul@319 463
protected:
paul@319 464
  Segment &_segment;
paul@319 465
paul@319 466
  /* Next element pointer. */
paul@319 467
paul@319 468
  l4_umword_t *_element;
paul@319 469
paul@319 470
  /* Stack section properties. */
paul@319 471
paul@319 472
  l4_addr_t _caps;
paul@319 473
  char *_arg_top, *_env_top;
paul@319 474
  char *_auxv_end;
paul@319 475
  int _env_entries;
paul@319 476
paul@319 477
public:
paul@319 478
  /* Start address and environment structure. */
paul@319 479
paul@319 480
  l4_addr_t start;
paul@319 481
  l4re_env_t *env;
paul@319 482
  l4_fpage_t utcb_fpage;
paul@319 483
paul@319 484
  /* Initialise a stack in a memory segment. */
paul@319 485
paul@319 486
  explicit Stack(Segment &segment, l4_fpage_t utcb_fpage)
paul@319 487
  : _segment(segment), utcb_fpage(utcb_fpage)
paul@319 488
  {
paul@319 489
    _element = (l4_umword_t *) (segment.buf + segment.size);
paul@319 490
  }
paul@319 491
paul@319 492
  /* Push any additional initial capabilities. */
paul@319 493
paul@319 494
  void push_cap_entries(l4re_env_cap_entry_t *entries)
paul@319 495
  {
paul@319 496
    l4re_env_cap_entry_t *entry = (l4re_env_cap_entry_t *) _element;
paul@319 497
paul@319 498
    *(--entry) = l4re_env_cap_entry_t();
paul@319 499
paul@319 500
    while ((entries != NULL) && (entries->cap != L4_INVALID_CAP))
paul@319 501
      *(--entry) = *entries;
paul@319 502
paul@319 503
    _caps = (l4_addr_t) entry;
paul@319 504
    _element = (l4_umword_t *) entry;
paul@319 505
  }
paul@319 506
paul@319 507
  /* Push environment values in reverse order. */
paul@319 508
paul@319 509
  void push_string(char *s)
paul@319 510
  {
paul@319 511
    char *arg = (char *) _element;
paul@319 512
    char *arg_last = arg;
paul@319 513
paul@319 514
    arg -= round(strlen(s) + 1, sizeof(l4_umword_t));
paul@319 515
paul@319 516
    memset(arg, 0, arg_last - arg);
paul@319 517
    memcpy(arg, s, strlen(s));
paul@319 518
paul@319 519
    _element = (l4_umword_t *) arg;
paul@319 520
  }
paul@319 521
paul@319 522
  /* Push environment values in reverse order. */
paul@319 523
paul@319 524
  void push_env(char *envp[])
paul@319 525
  {
paul@319 526
    _env_top = (char *) _element;
paul@319 527
    _env_entries = 0;
paul@319 528
paul@319 529
    for (; *envp != NULL; envp++, _env_entries++)
paul@319 530
      push_string(*envp);
paul@319 531
  }
paul@319 532
paul@319 533
  /* Push argument values in reverse order. */
paul@319 534
paul@319 535
  void push_args(int argc, char *argv[])
paul@319 536
  {
paul@319 537
    _arg_top = (char *) _element;
paul@319 538
paul@319 539
    for (int i = 0; i < argc; i++)
paul@319 540
      push_string(argv[i]);
paul@319 541
  }
paul@317 542
paul@317 543
  /* Loader flags, debugging flags, and the KIP capability index.
paul@317 544
     See: generate_l4aux in Remote_app_model */
paul@317 545
paul@319 546
  void push_l4re_flags()
paul@319 547
  {
paul@319 548
    *(--_element) = 0;
paul@319 549
    *(--_element) = 0;
paul@319 550
    *(--_element) = 0x14 << L4_CAP_SHIFT;
paul@319 551
  }
paul@319 552
paul@319 553
  void push_l4re_env()
paul@319 554
  {
paul@319 555
    env = (l4re_env_t *) _element;
paul@319 556
    env--;
paul@317 557
paul@319 558
    env->factory          = L4_BASE_FACTORY_CAP;
paul@319 559
    env->main_thread      = L4_BASE_THREAD_CAP;
paul@319 560
    env->log              = L4_BASE_LOG_CAP;
paul@319 561
    env->scheduler        = L4_BASE_SCHEDULER_CAP;
paul@319 562
    env->rm               = 0x11 << L4_CAP_SHIFT;
paul@319 563
    env->mem_alloc        = 0x12 << L4_CAP_SHIFT;
paul@319 564
    env->first_free_cap   = 0x15;
paul@319 565
    env->caps             = (l4re_env_cap_entry_t *) (_segment.region_address(_caps));
paul@319 566
    env->utcb_area        = utcb_fpage;
paul@319 567
    env->first_free_utcb  = l4_fpage_memaddr(utcb_fpage) + L4_UTCB_OFFSET;
paul@317 568
paul@319 569
    _element = (l4_umword_t *) env;
paul@319 570
  }
paul@317 571
paul@319 572
  /* Push the auxiliary vector. */
paul@319 573
paul@319 574
  void push_auxv()
paul@319 575
  {
paul@319 576
    _auxv_end = (char *) _element;
paul@319 577
paul@319 578
    struct auxv_entry *auxv_base = (struct auxv_entry *) _element;
paul@319 579
paul@319 580
    /* AUXV NULL. */
paul@319 581
paul@319 582
    *(--auxv_base) = {0, 0};
paul@317 583
paul@319 584
    /* L4Re global environment pointer. */
paul@317 585
paul@319 586
    *(--auxv_base) = {0xf1, _segment.region_address((char *) env)};
paul@317 587
paul@319 588
    /* Apparently required entries.
paul@319 589
       NOTE: The user/group identifiers should be obtained from the broader
paul@319 590
             environment. */
paul@317 591
paul@319 592
    *(--auxv_base) = {AT_PAGESZ, L4_PAGESIZE};
paul@319 593
    *(--auxv_base) = {AT_UID, 0};
paul@319 594
    *(--auxv_base) = {AT_EUID, 0};
paul@319 595
    *(--auxv_base) = {AT_GID, 0};
paul@319 596
    *(--auxv_base) = {AT_EGID, 0};
paul@317 597
paul@319 598
    _element = (l4_umword_t *) auxv_base;
paul@319 599
  }
paul@319 600
paul@319 601
  /* Fill the stack in reverse with an address, returning the size of the
paul@319 602
     value. */
paul@317 603
paul@319 604
  offset_t write_address(char *arg, char **addr, char *s)
paul@319 605
  {
paul@319 606
    offset_t size = round(strlen(s) + 1, sizeof(l4_umword_t));
paul@317 607
paul@319 608
    *addr = (char *) _segment.region_address(arg - size);
paul@319 609
    return size;
paul@319 610
  }
paul@317 611
paul@319 612
  /* Populate stack with environment pointers, employing a pointer ordering
paul@319 613
     that is the reverse of the value ordering. */
paul@319 614
paul@319 615
  void push_envp(char *envp[])
paul@319 616
  {
paul@319 617
    /* Write the terminating element. */
paul@317 618
paul@319 619
    *(--_element) = 0;
paul@317 620
paul@319 621
    /* Reserve space and fill the stack from the top inwards. */
paul@319 622
paul@319 623
    char **ep = (char **) (_element - _env_entries);
paul@319 624
    char *arg = _env_top;
paul@317 625
paul@319 626
    for (; *envp != NULL; envp++, ep++)
paul@319 627
      arg -= write_address(arg, ep, *envp);
paul@317 628
paul@319 629
    _element -= _env_entries;
paul@319 630
  }
paul@317 631
paul@319 632
  /* Populate stack with argument pointers and count, employing a pointer
paul@319 633
     ordering that is the reverse of the value ordering. */
paul@317 634
paul@319 635
  void push_argv(int argc, char *argv[])
paul@319 636
  {
paul@319 637
    /* Write the terminating element. */
paul@317 638
paul@319 639
    *(--_element) = 0;
paul@317 640
paul@319 641
    /* Reserve space and fill the stack from the top inwards. */
paul@319 642
paul@319 643
    char **ap = (char **) (_element - argc);
paul@319 644
    char *arg = _arg_top;
paul@318 645
paul@319 646
    for (int i = 0; i < argc; i++)
paul@319 647
      arg -= write_address(arg, &ap[i], argv[i]);
paul@318 648
paul@319 649
    /* Write the count. */
paul@319 650
paul@319 651
    _element -= argc;
paul@319 652
paul@319 653
    *(--_element) = argc;
paul@318 654
  }
paul@318 655
paul@319 656
  /* Adjust the stack alignment and return the stack address. */
paul@319 657
paul@319 658
  l4_addr_t align_stack()
paul@319 659
  {
paul@319 660
    char *current = (char *) _element;
paul@319 661
    char *adjusted = Ldr::adjust_sp(current, NULL);
paul@319 662
paul@319 663
    if (adjusted != current)
paul@319 664
      memmove(adjusted, (const void *) current, _auxv_end - current);
paul@319 665
paul@319 666
    _element = (l4_umword_t *) adjusted;
paul@319 667
paul@319 668
    return _segment.region_address(adjusted);
paul@319 669
  }
paul@319 670
paul@319 671
  /* Populate stack with additional capabilities. */
paul@317 672
paul@319 673
  void populate(int argc, char *argv[], char *envp[])
paul@319 674
  {
paul@319 675
    push_cap_entries(NULL);
paul@319 676
paul@319 677
    /* Populate stack with environment and argument values. */
paul@319 678
paul@319 679
    push_env(envp);
paul@319 680
    push_args(argc, argv);
paul@319 681
paul@319 682
    /* Push L4Re flags, environment and auxiliary vector. */
paul@319 683
paul@319 684
    push_l4re_flags();
paul@319 685
    push_l4re_env();
paul@319 686
    push_auxv();
paul@319 687
paul@319 688
    /* Push environment and argument pointers. */
paul@317 689
paul@319 690
    push_envp(envp);
paul@319 691
    push_argv(argc, argv);
paul@319 692
paul@319 693
    /* Adjust the stack alignment. */
paul@319 694
paul@319 695
    start = align_stack();
paul@319 696
  }
paul@319 697
};
paul@319 698
paul@319 699
paul@317 700
paul@319 701
class Environment
paul@319 702
{
paul@319 703
protected:
paul@319 704
  Stack &_st;
paul@319 705
  l4_cap_idx_t task = L4_INVALID_CAP, thread = L4_INVALID_CAP;
paul@319 706
  l4_cap_idx_t _pager_cap = 0x10 << L4_CAP_SHIFT;
paul@319 707
paul@319 708
public:
paul@319 709
  explicit Environment(Stack &st)
paul@319 710
  : _st(st)
paul@319 711
  {
paul@319 712
  }
paul@319 713
paul@319 714
  long create_task()
paul@319 715
  {
paul@319 716
    task = ipc_cap_alloc();
paul@319 717
paul@319 718
    if (l4_is_invalid_cap(task))
paul@319 719
      return -L4_ENOMEM;
paul@319 720
paul@319 721
    return l4_error(l4_factory_create_task(l4re_env()->factory, task, _st.utcb_fpage));
paul@319 722
  }
paul@317 723
paul@319 724
  long create_thread()
paul@319 725
  {
paul@319 726
    thread = ipc_cap_alloc();
paul@319 727
paul@319 728
    if (l4_is_invalid_cap(thread))
paul@319 729
      return -L4_ENOMEM;
paul@319 730
paul@319 731
    return l4_error(l4_factory_create_thread(l4re_env()->factory, thread));
paul@319 732
  }
paul@319 733
paul@319 734
  /* Configure the task environment. */
paul@317 735
paul@319 736
  long configure(l4_cap_idx_t server)
paul@319 737
  {
paul@319 738
    /* Map the KIP into the task. */
paul@319 739
paul@319 740
    l4_addr_t kip_start = (l4_addr_t) l4re_kip();
paul@319 741
paul@319 742
    long err = l4_error(l4_task_map(task, L4RE_THIS_TASK_CAP,
paul@319 743
                                    l4_fpage(kip_start, L4_PAGESHIFT, L4_FPAGE_RX),
paul@319 744
                                    kip_start));
paul@319 745
paul@319 746
    if (err)
paul@319 747
      return err;
paul@319 748
paul@319 749
    /* Define capability mappings for the new task. */
paul@319 750
paul@319 751
    l4re_env_t *env = _st.env;
paul@317 752
paul@319 753
    struct mapped_cap mapped_caps[] = {
paul@319 754
      {L4_BASE_TASK_CAP,  task,                   L4_CAP_FPAGE_RWS},
paul@319 755
      {_pager_cap,        server,                 L4_CAP_FPAGE_RWS},
paul@319 756
      {env->rm,           server,                 L4_CAP_FPAGE_RWS},
paul@319 757
      {env->main_thread,  thread,                 L4_CAP_FPAGE_RWS},
paul@319 758
      {env->factory,      l4re_env()->factory,    L4_CAP_FPAGE_RWS},
paul@319 759
      {env->log,          l4re_env()->log,        L4_CAP_FPAGE_RWS},
paul@319 760
      {env->scheduler,    l4re_env()->scheduler,  L4_CAP_FPAGE_RWS},
paul@319 761
      {env->mem_alloc,    l4re_env()->mem_alloc,  L4_CAP_FPAGE_RWS},
paul@319 762
      {0,                 L4_INVALID_CAP,         0},
paul@319 763
      };
paul@319 764
paul@319 765
    return map_capabilities(task, mapped_caps);
paul@319 766
  }
paul@319 767
paul@319 768
  /* Initialise the thread. */
paul@317 769
paul@319 770
  long thread_init(l4_addr_t program_start)
paul@319 771
  {
paul@319 772
    l4_thread_control_start();
paul@319 773
    l4_thread_control_pager(_pager_cap);
paul@319 774
    l4_thread_control_exc_handler(_pager_cap);
paul@319 775
    l4_thread_control_bind((l4_utcb_t *) l4_fpage_memaddr(_st.utcb_fpage), task);
paul@319 776
paul@319 777
    long err = l4_error(l4_thread_control_commit(thread));
paul@319 778
paul@319 779
    if (err)
paul@319 780
      return err;
paul@317 781
paul@319 782
    return l4_error(l4_thread_ex_regs(thread, program_start, _st.start, 0));
paul@319 783
  }
paul@319 784
paul@319 785
  /* Start the thread. */
paul@319 786
paul@319 787
  long thread_start()
paul@319 788
  {
paul@319 789
    l4_sched_param_t sp = l4_sched_param(L4RE_MAIN_THREAD_PRIO);
paul@319 790
paul@319 791
    return l4_error(l4_scheduler_run_thread(l4re_env()->scheduler, thread, &sp));
paul@319 792
  }
paul@319 793
};
paul@317 794
paul@317 795
paul@317 796
paul@308 797
static ExecPager exec_pager;
paul@308 798
paul@312 799
static void init_pager(ipc_server_config_type *config)
paul@308 800
{
paul@308 801
  ipc_server_init_config(config);
paul@308 802
paul@312 803
  config->expected_items = PagerObject_expected_items;
paul@312 804
  config->handler = (ipc_server_handler_type) handle_PagerObject;
paul@312 805
  config->handler_obj = static_cast<PagerObject *>(&exec_pager);
paul@308 806
}
paul@308 807
paul@308 808
static long start_pager(ipc_server_config_type *config, pthread_t thread)
paul@308 809
{
paul@308 810
  config->config_thread = 1;
paul@308 811
  config->thread = pthread_l4_cap(thread);
paul@308 812
paul@308 813
  printf("Starting pager thread...\n");
paul@308 814
  return ipc_server_start_config(config);
paul@308 815
}
paul@308 816
paul@308 817
paul@308 818
paul@308 819
int main(int argc, char *argv[])
paul@308 820
{
paul@308 821
  long err;
paul@308 822
paul@308 823
  if (argc < 2)
paul@308 824
  {
paul@308 825
    printf("Need a program to run.\n");
paul@308 826
    return 1;
paul@308 827
  }
paul@308 828
paul@308 829
  /* Obtain the payload as a dataspace. */
paul@308 830
paul@308 831
  file_t *file = client_open(argv[1], O_RDONLY);
paul@308 832
paul@308 833
  if (file == NULL)
paul@308 834
  {
paul@308 835
    printf("Could not read file: %s\n", argv[1]);
paul@308 836
    return 1;
paul@308 837
  }
paul@308 838
paul@312 839
  /* Copy the payload regions to new dataspaces.
paul@312 840
     NOTE: This should be directed by the ELF metadata. */
paul@308 841
paul@318 842
  address_t program_start = 0x1000b43;
paul@320 843
  offset_t initial_stack_size = 16 * L4_PAGESIZE;
paul@315 844
paul@318 845
  Segment program(0x1000000, 0x28326, L4_FPAGE_RWX, 0, 0x28326);
paul@316 846
  Segment data(0x1029360, 0x8068, L4_FPAGE_RW, 0x28360, 0x2058);
paul@320 847
  Segment stack(Utcb_area_start - initial_stack_size, initial_stack_size, L4_FPAGE_RW);
paul@316 848
  Segment *segments[] = {&program, &data, &stack, NULL};
paul@316 849
  Segment **segment;
paul@316 850
paul@316 851
  for (segment = segments; *segment != NULL; segment++)
paul@316 852
  {
paul@316 853
    err = (*segment)->allocate();
paul@308 854
paul@316 855
    if (err)
paul@316 856
    {
paul@316 857
      printf("Could not reserve memory.\n");
paul@316 858
      return 1;
paul@316 859
    }
paul@308 860
paul@316 861
    err = (*segment)->fill(file);
paul@316 862
paul@316 863
    if (err)
paul@316 864
    {
paul@316 865
      printf("Could not fill segment from file.\n");
paul@316 866
      return 1;
paul@316 867
    }
paul@308 868
  }
paul@308 869
paul@317 870
  /* Populate the stack with argument and environment details. */
paul@308 871
paul@319 872
  Stack st(stack, get_utcb_fpage());
paul@319 873
  Environment environment(st);
paul@319 874
paul@319 875
  /* NOTE: Environment vector is currently not defined. */
paul@319 876
paul@319 877
  char *envp[] = {NULL};
paul@319 878
paul@319 879
  st.populate(argc - 1, argv + 1, envp);
paul@312 880
paul@312 881
  /* Create a new task and thread. */
paul@308 882
paul@319 883
  err = environment.create_task();
paul@308 884
paul@308 885
  if (err)
paul@308 886
  {
paul@308 887
    printf("Could not create task.\n");
paul@308 888
    return 1;
paul@308 889
  }
paul@308 890
paul@319 891
  err = environment.create_thread();
paul@308 892
paul@308 893
  if (err)
paul@308 894
  {
paul@308 895
    printf("Could not create thread.\n");
paul@308 896
    return 1;
paul@308 897
  }
paul@308 898
paul@319 899
  err = environment.thread_init(program_start);
paul@319 900
paul@319 901
  if (err)
paul@319 902
  {
paul@319 903
    printf("Could not configure thread.\n");
paul@319 904
    return 1;
paul@319 905
  }
paul@319 906
paul@308 907
  /* Start the pager. */
paul@308 908
paul@308 909
  ipc_server_config_type config;
paul@308 910
  pthread_t pager_thread;
paul@308 911
  pthread_attr_t attr;
paul@308 912
paul@308 913
  pthread_attr_init(&attr);
paul@308 914
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
paul@308 915
paul@312 916
  init_pager(&config);
paul@312 917
paul@316 918
  for (segment = segments; *segment != NULL; segment++)
paul@316 919
    exec_pager.add((*segment)->region());
paul@308 920
paul@308 921
  err = pthread_create(&pager_thread, &attr, ipc_server_start_mainloop, &config);
paul@308 922
paul@308 923
  if (err)
paul@308 924
  {
paul@308 925
    printf("Could not start pager thread.\n");
paul@308 926
    return 1;
paul@308 927
  }
paul@308 928
paul@308 929
  err = start_pager(&config, pager_thread);
paul@308 930
paul@308 931
  if (err)
paul@308 932
  {
paul@308 933
    printf("Could not start pager.\n");
paul@308 934
    return 1;
paul@308 935
  }
paul@308 936
paul@319 937
  /* Configure the environment for the task, specifying the pager (and exception
paul@319 938
     handler plus region mapper). */
paul@308 939
paul@319 940
  err = environment.configure(config.server);
paul@308 941
paul@308 942
  if (err)
paul@308 943
  {
paul@319 944
    printf("Could not map capabilities into task.\n");
paul@308 945
    return 1;
paul@308 946
  }
paul@308 947
paul@308 948
  /* Start the new thread. */
paul@308 949
paul@308 950
  printf("Run thread...\n");
paul@308 951
paul@319 952
  err = environment.thread_start();
paul@308 953
paul@308 954
  if (err)
paul@308 955
  {
paul@308 956
    printf("Could not run thread.\n");
paul@308 957
    return 1;
paul@308 958
  }
paul@308 959
paul@308 960
  printf("Finished.\n");
paul@308 961
  while (1);
paul@308 962
paul@308 963
  return 0;
paul@308 964
}
paul@308 965
paul@308 966
/* vim: tabstop=2 expandtab shiftwidth=2
paul@308 967
*/