L4Re/departure

Annotated libexec/lib/src/process_creating.cc

524:86d76cd9a8d3
19 months ago Paul Boddie Made the process creation activity sequential and introduced a missing reset operation so that more than one process can be successfully created.
paul@477 1
/*
paul@477 2
 * Support for executing code in new tasks and threads.
paul@477 3
 *
paul@477 4
 * Copyright (C) 2022, 2023 Paul Boddie <paul@boddie.org.uk>
paul@477 5
 *
paul@477 6
 * This program is free software; you can redistribute it and/or
paul@477 7
 * modify it under the terms of the GNU General Public License as
paul@477 8
 * published by the Free Software Foundation; either version 2 of
paul@477 9
 * the License, or (at your option) any later version.
paul@477 10
 *
paul@477 11
 * This program is distributed in the hope that it will be useful,
paul@477 12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
paul@477 13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
paul@477 14
 * GNU General Public License for more details.
paul@477 15
 *
paul@477 16
 * You should have received a copy of the GNU General Public License
paul@477 17
 * along with this program; if not, write to the Free Software
paul@477 18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
paul@477 19
 * Boston, MA  02110-1301, USA
paul@477 20
 */
paul@477 21
paul@477 22
#include <l4/re/env.h>
paul@477 23
paul@506 24
#include <exec/common.h>
paul@505 25
#include <fsclient/client.h>
paul@489 26
#include <fsserver/resource_server.h>
paul@477 27
#include <ipc/cap_alloc.h>
paul@477 28
#include <ipc/map.h>
paul@511 29
#include <systypes/env.h>
paul@505 30
#include <systypes/fcntl.h>
paul@477 31
paul@477 32
#include <stdio.h>
paul@477 33
paul@483 34
#include "process_creating.h"
paul@477 35
paul@477 36
paul@477 37
paul@477 38
/* Process stack configuration. */
paul@477 39
paul@477 40
static const offset_t initial_stack_size = 16 * L4_PAGESIZE;
paul@477 41
paul@477 42
paul@477 43
paul@477 44
/* Initialise the process creator with the details of a region mapper. */
paul@477 45
paul@483 46
ProcessCreating::ProcessCreating(const char *rm_filename)
paul@495 47
: _rm_filename(rm_filename)
paul@477 48
{
paul@477 49
}
paul@477 50
paul@477 51
/* Initialise the memory segments of the region mapper. These are mapped into
paul@477 52
   this task so that we may access them, allowing the external pager in this
paul@477 53
   task to use them. */
paul@477 54
paul@483 55
long ProcessCreating::init_region_mapper()
paul@477 56
{
paul@477 57
  long err = exec_get_payload(_rm_filename, &_rm_payload, true);
paul@477 58
paul@477 59
  if (err)
paul@477 60
    return err;
paul@477 61
paul@504 62
  _rm_stack = new ExplicitSegment(Utcb_area_start - initial_stack_size, initial_stack_size, L4_FPAGE_RW);
paul@495 63
  return _rm_stack->allocate(true);
paul@477 64
}
paul@477 65
paul@477 66
/* Initialise the memory segments of the actual program. These are not mapped
paul@477 67
   into this task, instead being accessed by the region mapper in the new
paul@477 68
   task. */
paul@477 69
paul@483 70
long ProcessCreating::init_program(file_t *file)
paul@477 71
{
paul@477 72
  long err = exec_get_payload_file(file, &_program_payload, false);
paul@477 73
paul@477 74
  if (err)
paul@477 75
    return err;
paul@477 76
paul@504 77
  _program_stack = new ExplicitSegment(Utcb_area_start - initial_stack_size * 2, initial_stack_size, L4_FPAGE_RW);
paul@495 78
  return _program_stack->allocate(true);
paul@477 79
}
paul@477 80
paul@477 81
/* Initialise an external system-level pager serving the region mapper in a
paul@477 82
   created task. The allocated regions requested by the region mapper are
paul@477 83
   constrained to an area of memory that must not overlap with the area reserved
paul@477 84
   for the program being run. */
paul@477 85
paul@489 86
long ProcessCreating::init_external_pager(l4_cap_idx_t *pager)
paul@477 87
{
paul@487 88
  _exec_pager = new ExternalPager(0, 10 * L4_PAGESIZE);
paul@487 89
paul@477 90
  /* Initialise pager regions for the region mapper. */
paul@477 91
paul@477 92
  for (unsigned int i = 0; i < _rm_payload->segments(); i++)
paul@477 93
  {
paul@477 94
    if (_rm_payload->segment(i)->loadable())
paul@487 95
      _exec_pager->add(_rm_payload->segment(i)->region());
paul@477 96
  }
paul@477 97
paul@477 98
  /* Include the region mapper's stack region. */
paul@477 99
paul@495 100
  _exec_pager->add(_rm_stack->region());
paul@504 101
  _exec_pager->set_payload(_rm_payload);
paul@504 102
  _exec_pager->set_stack(_rm_stack);
paul@477 103
paul@477 104
  /* Start the pager in a separate thread. */
paul@477 105
paul@492 106
  long err = ResourceServer(_exec_pager).start_thread(pager);
paul@492 107
paul@492 108
  if (err)
paul@492 109
    return err;
paul@492 110
paul@492 111
  return L4_EOK;
paul@477 112
}
paul@477 113
paul@477 114
/* Configure the environment for the task. */
paul@477 115
paul@489 116
long ProcessCreating::configure_task(l4_cap_idx_t pager)
paul@477 117
{
paul@503 118
  l4_cap_idx_t task, mapped_task;
paul@503 119
  long err = _process.configure_task(&task, &mapped_task);
paul@477 120
paul@477 121
  if (err)
paul@477 122
    return err;
paul@477 123
paul@503 124
  /* Record the task details in the pager for eventual resource deallocation. */
paul@503 125
paul@503 126
  _exec_pager->set_task(task, mapped_task);
paul@501 127
paul@503 128
  /* Note the pager as the parent of the new task, recording its capability
paul@503 129
     details in the new task. */
paul@501 130
paul@503 131
  l4_cap_idx_t mapped_parent;
paul@503 132
  err = _process.set_parent(pager, &mapped_parent);
paul@499 133
paul@499 134
  if (err)
paul@499 135
    return err;
paul@499 136
paul@503 137
  _exec_pager->set_parent(pager, mapped_parent);
paul@499 138
  return L4_EOK;
paul@477 139
}
paul@477 140
paul@477 141
/* Create an unbound IPC gate for the region mapper and allocate it in the
paul@477 142
   created process. */
paul@477 143
paul@483 144
long ProcessCreating::create_ipc_gate()
paul@477 145
{
paul@477 146
  _ipc_gate_cap = _process.allocate_cap();
paul@477 147
  _ipc_gate = ipc_cap_alloc();
paul@477 148
paul@477 149
  if (l4_is_invalid_cap(_ipc_gate))
paul@477 150
    return -L4_ENOMEM;
paul@477 151
paul@503 152
  long err = l4_error(l4_factory_create_gate(l4re_env()->factory, _ipc_gate, L4_INVALID_CAP, 0));
paul@503 153
paul@503 154
  if (err)
paul@503 155
    return err;
paul@503 156
paul@503 157
  /* The gate is retained because even after being mapped to the new task,
paul@503 158
     releasing it will cause it to be deallocated. */
paul@503 159
paul@501 160
  _exec_pager->set_gate(_ipc_gate);
paul@503 161
  return L4_EOK;
paul@477 162
}
paul@477 163
paul@477 164
/* Initialise and assign a region in a list to the created process. */
paul@477 165
paul@483 166
void ProcessCreating::init_region(struct exec_region *regions,
paul@477 167
                                 struct ipc_mapped_cap *mapped_caps,
paul@477 168
                                 struct exec_region &r, unsigned int &index)
paul@477 169
{
paul@477 170
  l4_cap_idx_t mapped_cap = _process.allocate_cap();
paul@477 171
paul@477 172
  mapped_caps[index] = (struct ipc_mapped_cap) {mapped_cap, r.ds, L4_CAP_FPAGE_RWS, 0};
paul@477 173
paul@477 174
  /* Change the region definition to use the allocated capability in the created
paul@477 175
     process. */
paul@477 176
paul@477 177
  regions[index] = r;
paul@477 178
  regions[index].ds = mapped_cap;
paul@477 179
  index++;
paul@477 180
}
paul@477 181
paul@477 182
/* Initialise the region mapper with details of the payload program regions
paul@482 183
   and of the associated capabilities, configure the region mapper thread,
paul@482 184
   populate its stack, and start the thread. */
paul@477 185
paul@489 186
long ProcessCreating::start_region_mapper(l4_cap_idx_t pager)
paul@477 187
{
paul@477 188
  /* Define regions employing dataspaces to provide program segments. */
paul@477 189
paul@496 190
  struct exec_region rm_regions[_program_payload->segments() + 2];
paul@477 191
paul@477 192
  /* Define capabilities for mapping, including region dataspace capabilities,
paul@511 193
     the stack dataspace capability, plus the pager capability. */
paul@477 194
paul@496 195
  struct ipc_mapped_cap rm_mapped_caps[_program_payload->segments() + 3];
paul@477 196
paul@477 197
  /* Here, the arrays are sized for the maximum number of regions and
paul@477 198
     capabilities, but in practice only the loadable segments are used, leaving
paul@477 199
     fewer elements utilised. A terminating entry is employed to indicate the
paul@477 200
     limit of utilised elements. */
paul@477 201
paul@477 202
  unsigned int rm_index = 0;
paul@477 203
paul@477 204
  for (unsigned int i = 0; i < _program_payload->segments(); i++)
paul@477 205
  {
paul@477 206
    Segment *s = _program_payload->segment(i);
paul@477 207
paul@477 208
    if (s->loadable())
paul@477 209
      init_region(rm_regions, rm_mapped_caps, s->exec_region(), rm_index);
paul@477 210
  }
paul@477 211
paul@477 212
  /* Introduce the stack region and capability. */
paul@477 213
paul@495 214
  init_region(rm_regions, rm_mapped_caps, _program_stack->exec_region(), rm_index);
paul@477 215
paul@477 216
  /* Terminate the region array. */
paul@477 217
paul@477 218
  rm_regions[rm_index] = (struct exec_region) {0, 0, 0, L4_INVALID_CAP};
paul@477 219
paul@477 220
  /* Introduce the server capability and terminate the capability array. */
paul@477 221
paul@477 222
  rm_mapped_caps[rm_index++] = (struct ipc_mapped_cap) {_ipc_gate_cap, _ipc_gate, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS};
paul@477 223
  rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {0, L4_INVALID_CAP, 0, 0};
paul@477 224
paul@477 225
  /* Map these additional capabilities. */
paul@477 226
paul@511 227
  long err = _process.map_capabilities(rm_mapped_caps, false);
paul@511 228
paul@511 229
  if (err)
paul@511 230
    return err;
paul@477 231
paul@477 232
  /* Define the IPC gate as an initial capability to be acquired by the region
paul@477 233
     mapper via the l4re_env API. The capability index is assigned above when
paul@477 234
     mapping the capability and encoded in the entry below. */
paul@477 235
paul@477 236
  l4re_env_cap_entry_t rm_init_caps[] = {
paul@506 237
    l4re_env_cap_entry_t(ENV_INTERNAL_PAGER_NAME, _ipc_gate_cap, L4_CAP_FPAGE_RWS),
paul@477 238
    l4re_env_cap_entry_t()
paul@477 239
    };
paul@477 240
paul@477 241
  /* NOTE: Environment vector is currently not defined. */
paul@477 242
paul@477 243
  const char *envp[] = {NULL};
paul@477 244
paul@477 245
  /* Configure the environment for the thread, specifying the pager (and
paul@477 246
     exception handler plus region mapper). */
paul@477 247
paul@503 248
  l4_cap_idx_t mapped_pager = L4_INVALID_CAP;
paul@511 249
  err = _process.configure_thread(pager, &mapped_pager);
paul@477 250
paul@477 251
  if (err)
paul@477 252
    return err;
paul@477 253
paul@503 254
  _exec_pager->set_pager(pager, mapped_pager);
paul@499 255
paul@477 256
  /* Populate a thread stack with argument and environment details for the
paul@477 257
     region mapper, plus the initial server capability and region details. */
paul@477 258
paul@477 259
  const char *argv[] = {_rm_filename};
paul@495 260
  Stack rm_st(*_rm_stack);
paul@477 261
paul@477 262
  rm_st.set_init_caps(rm_init_caps);
paul@477 263
  rm_st.set_regions(rm_regions);
paul@477 264
  rm_st.populate(1, argv, envp);
paul@477 265
paul@477 266
  /* Start the region mapper thread in the appropriate stack. */
paul@477 267
paul@503 268
  l4_cap_idx_t thread, mapped_thread;
paul@503 269
  err = _process.thread_start(_rm_payload->entry_point(), rm_st, &thread, &mapped_thread);
paul@492 270
paul@492 271
  if (err)
paul@492 272
    return err;
paul@492 273
paul@503 274
  _exec_pager->add_thread(thread, mapped_thread);
paul@492 275
  return L4_EOK;
paul@477 276
}
paul@477 277
paul@482 278
/* Configure a thread for a program, populate its stack, and start the
paul@482 279
   thread. */
paul@482 280
paul@483 281
long ProcessCreating::start_program(int argc, const char *argv[])
paul@477 282
{
paul@477 283
  /* NOTE: Environment vector is currently not defined. */
paul@477 284
paul@477 285
  const char *envp[] = {NULL};
paul@477 286
paul@477 287
  /* Configure the environment for the thread, specifying the pager (and
paul@477 288
     exception handler plus region mapper). */
paul@477 289
paul@503 290
  l4_cap_idx_t mapped_pager = _ipc_gate_cap;
paul@503 291
  long err = _process.configure_thread(_ipc_gate, &mapped_pager);
paul@477 292
paul@477 293
  if (err)
paul@477 294
    return err;
paul@477 295
paul@511 296
  /* Obtain the filesystem capability for exporting to the task. */
paul@511 297
paul@511 298
  l4_cap_idx_t fsserver_cap = _process.allocate_cap();
paul@511 299
  l4_cap_idx_t fsserver = l4re_env_get_cap(ENV_FILESYSTEM_SERVER_NAME);
paul@511 300
paul@511 301
  /* Define the capabilities to be mapped for the filesystem. */
paul@511 302
paul@511 303
  struct ipc_mapped_cap program_mapped_caps[] = {
paul@511 304
    {fsserver_cap, fsserver, L4_CAP_FPAGE_RWS, L4_FPAGE_C_OBJ_RIGHTS},
paul@511 305
    {0, L4_INVALID_CAP, 0, 0},
paul@511 306
    };
paul@511 307
paul@511 308
  /* Map these additional capabilities. */
paul@511 309
paul@511 310
  err = _process.map_capabilities(program_mapped_caps, false);
paul@511 311
paul@511 312
  if (err)
paul@511 313
    return err;
paul@511 314
paul@511 315
  /* Define initial capabilities to be acquired by the region mapper via the
paul@511 316
     l4re_env API. Each capability index is assigned above when mapping the
paul@511 317
     capability and encoded in the entry below. */
paul@511 318
paul@511 319
  l4re_env_cap_entry_t program_init_caps[] = {
paul@511 320
    l4re_env_cap_entry_t(ENV_FILESYSTEM_SERVER_NAME, fsserver_cap, L4_CAP_FPAGE_RWS),
paul@511 321
    l4re_env_cap_entry_t()
paul@511 322
    };
paul@511 323
paul@477 324
  /* Populate a thread stack with argument and environment details for the
paul@477 325
     actual program. The server capability should be assigned to the region
paul@477 326
     mapper capability slot already. */
paul@477 327
paul@495 328
  Stack program_st(*_program_stack);
paul@477 329
paul@511 330
  program_st.set_init_caps(program_init_caps);
paul@477 331
  program_st.populate(argc, argv, envp);
paul@477 332
paul@477 333
  /* Start the program thread in the appropriate stack. */
paul@477 334
paul@503 335
  l4_cap_idx_t thread, mapped_thread;
paul@503 336
  err = _process.thread_start(_program_payload->entry_point(), program_st, &thread, &mapped_thread);
paul@492 337
paul@492 338
  if (err)
paul@492 339
    return err;
paul@492 340
paul@503 341
  _exec_pager->add_thread(thread, mapped_thread);
paul@492 342
  return L4_EOK;
paul@477 343
}
paul@477 344
paul@505 345
/* Start a new process for the payload indicated by the first of the given
paul@505 346
   program arguments, returning a reference to the pager as an object for
paul@505 347
   interacting with the process. */
paul@505 348
paul@505 349
long ProcessCreating::start(int argc, const char *argv[], l4_cap_idx_t *process)
paul@505 350
{
paul@524 351
  std::lock_guard<std::mutex> guard(_lock);
paul@505 352
paul@505 353
  /* Open the program file, handling any error conditions. If successfully
paul@505 354
     opened, it will be closed when the process terminates. */
paul@482 355
paul@524 356
  file_t *file = client_open(argv[0], O_RDONLY);
paul@524 357
  long err;
paul@524 358
paul@505 359
  if (file == NULL)
paul@505 360
    return -L4_EIO;
paul@505 361
paul@505 362
  if (!client_opened(file))
paul@505 363
  {
paul@505 364
    err = file->error;
paul@505 365
    client_close(file);
paul@505 366
    return err;
paul@505 367
  }
paul@505 368
paul@505 369
  /* Initialise the different elements of the process. */
paul@477 370
paul@477 371
  err = init_region_mapper();
paul@477 372
  if (err)
paul@477 373
    return err;
paul@477 374
paul@477 375
  err = init_program(file);
paul@477 376
  if (err)
paul@477 377
    return err;
paul@477 378
paul@489 379
  err = init_external_pager(process);
paul@477 380
  if (err)
paul@477 381
    return err;
paul@477 382
paul@489 383
  err = configure_task(*process);
paul@477 384
  if (err)
paul@477 385
    return err;
paul@477 386
paul@477 387
  err = create_ipc_gate();
paul@477 388
  if (err)
paul@477 389
    return err;
paul@477 390
paul@489 391
  err = start_region_mapper(*process);
paul@477 392
  if (err)
paul@477 393
    return err;
paul@477 394
paul@487 395
  err = start_program(argc, argv);
paul@487 396
  if (err)
paul@487 397
    return err;
paul@487 398
paul@495 399
  /* Discard instances created to initialise the process. The region mapper
paul@495 400
     relies on resources associated with its payload and stack and so these
paul@505 401
     cannot be deleted immediately. Instead, they are released when the pager is
paul@505 402
     deallocated.
paul@495 403
paul@487 404
     NOTE: The region mapper payload could be retained instead of being
paul@487 405
           reconstructed each time. */
paul@482 406
paul@487 407
  delete _program_payload;
paul@495 408
  delete _program_stack;
paul@487 409
paul@487 410
  return L4_EOK;
paul@480 411
}
paul@480 412
paul@477 413
/* vim: tabstop=2 expandtab shiftwidth=2
paul@477 414
*/