L4Re/departure

Annotated tests/dstest_exec.cc

370:65230d933d16
2022-06-13 Paul Boddie Fixed capability index allocation, introducing control over such allocation in the process abstraction, also allocating separate indexes for the region mapper in different threads. A separate pager capability has been eliminated. Fixed process initialisation, separating the preparation of each thread, since the process abstraction maintains a common environment structure, and only one thread should therefore be initialised at a time. mmap-region-flags
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@312 24
#include <l4/util/util.h>
paul@308 25
paul@327 26
#include <exec/elf.h>
paul@370 27
#include <exec/external_pager.h>
paul@349 28
#include <exec/memory.h>
paul@324 29
#include <exec/process.h>
paul@370 30
#include <ipc/cap_alloc.h>
paul@370 31
#include <ipc/map.h>
paul@308 32
#include <ipc/server.h>
paul@312 33
paul@308 34
#include <stdio.h>
paul@308 35
paul@308 36
#include <pthread-l4.h>
paul@308 37
#include <pthread.h>
paul@308 38
paul@312 39
#include "pager_object_server.h"
paul@308 40
paul@308 41
paul@308 42
paul@366 43
static ExternalPager exec_pager;
paul@308 44
paul@369 45
static const offset_t initial_stack_size = 16 * L4_PAGESIZE;
paul@369 46
paul@369 47
paul@369 48
paul@369 49
/* Start the system pager in a separate thread. */
paul@369 50
paul@369 51
static long start_pager(ipc_server_config_type &config)
paul@369 52
{
paul@369 53
  pthread_t pager_thread;
paul@369 54
  pthread_attr_t attr;
paul@369 55
paul@369 56
  pthread_attr_init(&attr);
paul@369 57
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
paul@369 58
paul@369 59
  ipc_server_init_for(&config, PagerObject, &exec_pager);
paul@369 60
paul@369 61
  long err = pthread_create(&pager_thread, &attr, ipc_server_start_mainloop, &config);
paul@369 62
paul@369 63
  if (err)
paul@369 64
    return err;
paul@369 65
paul@369 66
  return ipc_server_start_config_thread(&config, pthread_l4_cap(pager_thread));
paul@369 67
}
paul@369 68
paul@308 69
paul@308 70
paul@308 71
int main(int argc, char *argv[])
paul@308 72
{
paul@308 73
  long err;
paul@308 74
paul@370 75
  if (argc < 3)
paul@308 76
  {
paul@370 77
    printf("Need a program to run as the region mapper and a main program.\n");
paul@370 78
    return 1;
paul@370 79
  }
paul@370 80
paul@370 81
  /* Define the different payloads. */
paul@370 82
paul@370 83
  char *rm_filename = argv[1];
paul@370 84
  char *program_filename = argv[2];
paul@370 85
paul@370 86
  /* Initialise the memory segments of the region mapper. These are mapped into
paul@370 87
     this task so that we may access them. */
paul@370 88
paul@370 89
  ExplicitSegment rm_stack(Utcb_area_start - initial_stack_size, initial_stack_size, L4_FPAGE_RW);
paul@370 90
  Payload *rm_payload;
paul@370 91
paul@370 92
  if (exec_get_payload(rm_filename, &rm_payload, true))
paul@370 93
  {
paul@370 94
    printf("Could not initialise region mapper: %s\n", rm_filename);
paul@370 95
    return 1;
paul@370 96
  }
paul@370 97
paul@370 98
  if (rm_stack.allocate(true))
paul@370 99
  {
paul@370 100
    printf("Could not allocate region mapper stack.\n");
paul@308 101
    return 1;
paul@308 102
  }
paul@308 103
paul@370 104
  /* Initialise the memory segments of the actual program. These are not mapped
paul@370 105
     into this task, instead being accessed by the region mapper in the new
paul@370 106
     task. */
paul@323 107
paul@370 108
  ExplicitSegment program_stack(Utcb_area_start - initial_stack_size * 2, initial_stack_size, L4_FPAGE_RW);
paul@370 109
  Payload *program_payload;
paul@323 110
paul@370 111
  if (exec_get_payload(program_filename, &program_payload, false))
paul@323 112
  {
paul@370 113
    printf("Could not initialise program: %s\n", program_filename);
paul@325 114
    return 1;
paul@325 115
  }
paul@325 116
paul@370 117
  if (program_stack.allocate(true))
paul@316 118
  {
paul@370 119
    printf("Could not allocate program stack.\n");
paul@349 120
    return 1;
paul@308 121
  }
paul@308 122
paul@370 123
  /* Initialise pager regions for the region mapper. */
paul@312 124
paul@370 125
  for (unsigned int i = 0; i < rm_payload->segments(); i++)
paul@325 126
  {
paul@370 127
    if (rm_payload->segment(i)->loadable())
paul@370 128
      exec_pager.add(rm_payload->segment(i)->region());
paul@349 129
  }
paul@325 130
paul@370 131
  exec_pager.add(rm_stack.region());
paul@308 132
paul@369 133
  /* Start the pager in a separate thread. */
paul@354 134
paul@354 135
  ipc_server_config_type config;
paul@308 136
paul@354 137
  printf("Starting pager thread...\n");
paul@369 138
paul@369 139
  err = start_pager(config);
paul@308 140
paul@308 141
  if (err)
paul@308 142
  {
paul@308 143
    printf("Could not start pager.\n");
paul@308 144
    return 1;
paul@308 145
  }
paul@308 146
paul@370 147
  /* Configure the environment for the task, reserving two threads. */
paul@308 148
paul@370 149
  Process process(2);
paul@321 150
paul@365 151
  err = process.configure_task();
paul@308 152
paul@308 153
  if (err)
paul@308 154
  {
paul@321 155
    printf("Could not configure task.\n");
paul@308 156
    return 1;
paul@308 157
  }
paul@308 158
paul@370 159
  /* Configure the environment for the thread, specifying the pager (and
paul@370 160
     exception handler plus region mapper). */
paul@370 161
paul@365 162
  err = process.configure_thread(config.server);
paul@365 163
paul@365 164
  if (err)
paul@365 165
  {
paul@365 166
    printf("Could not configure thread.\n");
paul@365 167
    return 1;
paul@365 168
  }
paul@365 169
paul@370 170
  /* Create an unbound IPC gate for the region mapper. */
paul@370 171
paul@370 172
  l4_cap_idx_t ipc_gate = ipc_cap_alloc();
paul@370 173
paul@370 174
  if (l4_is_invalid_cap(ipc_gate))
paul@370 175
  {
paul@370 176
    printf("Could not allocate IPC gate capability.\n");
paul@370 177
    return 1;
paul@370 178
  }
paul@370 179
paul@370 180
  err = l4_error(l4_factory_create_gate(l4re_env()->factory, ipc_gate, L4_INVALID_CAP, 0));
paul@370 181
paul@370 182
  if (err)
paul@370 183
  {
paul@370 184
    printf("Could not create IPC gate.\n");
paul@370 185
    return 1;
paul@370 186
  }
paul@370 187
paul@370 188
  /* Define regions employing dataspaces to provide program segments.
paul@370 189
paul@370 190
     Define capabilities for mapping, including region dataspace capabilities,
paul@370 191
     the stack dataspace capability, and the server capability.
paul@370 192
paul@370 193
     Here, the arrays are sized for the maximum number of regions and
paul@370 194
     capabilities, but in practice only the loadable segments are used, leaving
paul@370 195
     fewer elements utilised. A terminating entry is employed to indicate the
paul@370 196
     limit of utilised elements. */
paul@370 197
paul@370 198
  struct exec_region rm_regions[rm_payload->segments() + 2];
paul@370 199
  struct ipc_mapped_cap rm_mapped_caps[rm_payload->segments() + 3];
paul@370 200
  unsigned int rm_index = 0;
paul@370 201
paul@370 202
  for (unsigned int i = 0; i < rm_payload->segments(); i++)
paul@370 203
  {
paul@370 204
    Segment *s = rm_payload->segment(i);
paul@321 205
paul@370 206
    if (s->loadable())
paul@370 207
    {
paul@370 208
      rm_regions[rm_index] = s->exec_region();
paul@370 209
      rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {process.allocate_cap(), rm_regions[rm_index].ds, L4_CAP_FPAGE_RWS};
paul@370 210
      rm_index++;
paul@370 211
    }
paul@370 212
  }
paul@370 213
paul@370 214
  /* Introduce the stack region and capability. */
paul@370 215
paul@370 216
  rm_regions[rm_index] = program_stack.exec_region();
paul@370 217
  rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {process.allocate_cap(), program_stack.exec_region().ds, L4_CAP_FPAGE_RWS};
paul@370 218
  rm_index++;
paul@370 219
paul@370 220
  /* Terminate the region array. */
paul@370 221
paul@370 222
  rm_regions[rm_index] = (struct exec_region) {0, 0, 0, L4_INVALID_CAP};
paul@370 223
paul@370 224
  /* Introduce the server capability. */
paul@370 225
paul@370 226
  l4_cap_idx_t ipc_gate_cap = process.allocate_cap();
paul@370 227
paul@370 228
  rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {ipc_gate_cap, ipc_gate, L4_CAP_FPAGE_RWS};
paul@370 229
  rm_index++;
paul@370 230
paul@370 231
  /* Terminate the capability array. */
paul@370 232
paul@370 233
  rm_mapped_caps[rm_index] = (struct ipc_mapped_cap) {0, L4_INVALID_CAP, 0};
paul@370 234
paul@370 235
  /* Map these additional capabilities. */
paul@370 236
paul@370 237
  printf("Map additional capabilities...\n");
paul@370 238
paul@370 239
  process.map_capabilities(rm_mapped_caps);
paul@370 240
paul@370 241
  /* Define the IPC gate as an initial capability to be acquired by the region
paul@370 242
     mapper via the l4re_env API. The capability index is assigned above when
paul@370 243
     mapping the capability and encoded in the entry below. */
paul@370 244
paul@370 245
  l4re_env_cap_entry_t rm_init_caps[] = {
paul@370 246
    l4re_env_cap_entry_t("server", ipc_gate_cap, L4_CAP_FPAGE_RWS),
paul@370 247
    l4re_env_cap_entry_t()
paul@370 248
    };
paul@321 249
paul@321 250
  /* NOTE: Environment vector is currently not defined. */
paul@321 251
paul@321 252
  char *envp[] = {NULL};
paul@321 253
paul@370 254
  /* Populate a thread stack with argument and environment details for the
paul@370 255
     region mapper, plus the initial server capability and region details. */
paul@321 256
paul@370 257
  printf("Populating region mapper stack...\n");
paul@370 258
paul@370 259
  Stack rm_st(rm_stack);
paul@308 260
paul@370 261
  rm_st.set_init_caps(rm_init_caps);
paul@370 262
  rm_st.set_regions(rm_regions);
paul@370 263
  rm_st.populate(1, argv + 1, envp);
paul@308 264
paul@370 265
  /* Start the region mapper thread in the appropriate stack. */
paul@370 266
paul@370 267
  printf("Run region mapper thread...\n");
paul@370 268
paul@370 269
  err = process.thread_start(rm_payload->entry_point(), rm_st);
paul@308 270
paul@308 271
  if (err)
paul@308 272
  {
paul@370 273
    printf("Could not run thread for region mapper.\n");
paul@370 274
    return 1;
paul@370 275
  }
paul@370 276
paul@370 277
  /* Configure the environment for the thread, specifying the pager (and
paul@370 278
     exception handler plus region mapper). */
paul@370 279
paul@370 280
  err = process.configure_thread(ipc_gate);
paul@370 281
paul@370 282
  if (err)
paul@370 283
  {
paul@370 284
    printf("Could not configure task.\n");
paul@370 285
    return 1;
paul@370 286
  }
paul@370 287
paul@370 288
  /* Populate a thread stack with argument and environment details for the
paul@370 289
     actual program. The server capability should be assigned to the region
paul@370 290
     mapper capability slot already. */
paul@370 291
paul@370 292
  printf("Populating program stack...\n");
paul@370 293
paul@370 294
  Stack program_st(program_stack);
paul@370 295
paul@370 296
  program_st.populate(argc - 2, argv + 2, envp);
paul@370 297
paul@370 298
  /* Start the program thread in the appropriate stack. */
paul@370 299
paul@370 300
  printf("Run program thread...\n");
paul@370 301
paul@370 302
  err = process.thread_start(program_payload->entry_point(), program_st);
paul@370 303
paul@370 304
  if (err)
paul@370 305
  {
paul@370 306
    printf("Could not run thread for program.\n");
paul@308 307
    return 1;
paul@308 308
  }
paul@308 309
paul@308 310
  printf("Finished.\n");
paul@369 311
  while (1)
paul@369 312
    l4_sleep_forever();
paul@308 313
paul@308 314
  return 0;
paul@308 315
}
paul@308 316
paul@308 317
/* vim: tabstop=2 expandtab shiftwidth=2
paul@308 318
*/