L4Re/departure

Annotated tests/dstest_exec.cc

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