FreeNOS
spawn.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2015 Niek Linnenbank
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <FreeNOS/System.h>
19 #include <FileSystemClient.h>
20 #include <FileDescriptor.h>
21 #include <ExecutableFormat.h>
22 #include <Types.h>
23 #include <Runtime.h>
24 #include "limits.h"
25 #include "string.h"
26 #include "errno.h"
27 #include "unistd.h"
28 
29 int spawn(Address program, Size programSize, const char *argv[])
30 {
31  const FileSystemClient filesystem;
32  ExecutableFormat *fmt;
33  ExecutableFormat::Region regions[16];
34  Arch::MemoryMap map;
35  Memory::Range range;
36  uint count = 0;
37  pid_t pid = 0;
38  Size numRegions = 16;
39  Address entry;
40 
41  // Attempt to read executable format
42  if (ExecutableFormat::find((u8 *) program, programSize, &fmt) != ExecutableFormat::Success)
43  {
44  errno = ENOEXEC;
45  return -1;
46  }
47 
48  // Find entry point
50  {
51  delete fmt;
52  errno = ENOEXEC;
53  return -1;
54  }
55 
56  // Create new process
57  const ulong result = ProcessCtl(ANY, Spawn, entry);
58  if ((result & 0xffff) != API::Success)
59  {
60  delete fmt;
61  errno = EIO;
62  return -1;
63  }
64  pid = (result >> 16);
65 
66  // Retrieve memory regions
67  if (fmt->regions(regions, &numRegions) != ExecutableFormat::Success)
68  {
69  delete fmt;
70  errno = ENOEXEC;
71  ProcessCtl(pid, KillPID);
72  return -1;
73  }
74  // Release buffers
75  delete fmt;
76 
77  // Map program regions into virtual memory of the new process
78  for (Size i = 0; i < numRegions; i++)
79  {
80  // Setup memory range to copy region data
81  range.virt = regions[i].virt;
82  range.phys = ZERO;
83  range.size = regions[i].memorySize;
84  range.access = regions[i].access;
85 
86  // Create mapping first in the new process
87  if (VMCtl(pid, MapContiguous, &range) != API::Success)
88  {
89  errno = EFAULT;
90  ProcessCtl(pid, KillPID);
91  return -1;
92  }
93 
94  // Map inside our process
95  range.virt = ZERO;
96  if (VMCtl(SELF, MapContiguous, &range) != API::Success)
97  {
98  errno = EFAULT;
99  ProcessCtl(pid, KillPID);
100  return -1;
101  }
102 
103  // Copy data bytes
104  MemoryBlock::copy((void *)range.virt, (const void *)(program + regions[i].dataOffset),
105  regions[i].dataSize);
106 
107  // Nulify remaining space
108  if (regions[i].memorySize > regions[i].dataSize)
109  {
110  MemoryBlock::set((void *)(range.virt + regions[i].dataSize), 0,
111  regions[i].memorySize - regions[i].dataSize);
112  }
113 
114  // Remove temporary mapping
115  if (VMCtl(SELF, UnMap, &range) != API::Success)
116  {
117  errno = EFAULT;
118  ProcessCtl(pid, KillPID);
119  return -1;
120  }
121  }
122 
123  // Create mapping for command-line arguments
124  range = map.range(MemoryMap::UserArgs);
125  range.phys = ZERO;
127  if (VMCtl(pid, MapContiguous, &range) != API::Success)
128  {
129  errno = EFAULT;
130  ProcessCtl(pid, KillPID);
131  return -1;
132  }
133 
134  // Allocate arguments and current working directory
135  char *arguments = new char[PAGESIZE*2];
136  memset(arguments, 0, PAGESIZE*2);
137 
138  // Fill in arguments
139  while (argv[count] && count < PAGESIZE / ARGV_SIZE)
140  {
141  strlcpy(arguments + (ARGV_SIZE * count), argv[count], ARGV_SIZE);
142  count++;
143  }
144 
145  // Fill in the current working directory
146  strlcpy(arguments + PAGESIZE, **filesystem.getCurrentDirectory(), PATH_MAX);
147 
148  // Copy argc/argv into the new process
149  if (VMCopy(pid, API::Write, (Address) arguments, range.virt, PAGESIZE * 2) != API::Success)
150  {
151  delete[] arguments;
152  errno = EFAULT;
153  ProcessCtl(pid, KillPID);
154  return -1;
155  }
156 
157  // Copy fds into the new process.
158  if (VMCopy(pid, API::Write, (Address) FileDescriptor::instance()->getArray(count),
159  range.virt + (PAGESIZE * 2), range.size - (PAGESIZE * 2)) != API::Success)
160  {
161  delete[] arguments;
162  errno = EFAULT;
163  ProcessCtl(pid, KillPID);
164  return -1;
165  }
166 
167  // Let the Child begin execution
168  if (ProcessCtl(pid, Resume) != API::Success)
169  {
170  delete[] arguments;
171  errno = EIO;
172  ProcessCtl(pid, KillPID);
173  return -1;
174  }
175 
176  // Done. Cleanup.
177  delete[] arguments;
178  return pid;
179 }
ExecutableFormat.h
strlcpy
size_t strlcpy(char *dst, const char *src, size_t siz)
Copy src to string dst of size siz.
Definition: strlcpy.cpp:19
Runtime.h
MemoryBlock::copy
static Size copy(void *dest, const void *src, Size count)
Copy memory from one place to another.
Definition: MemoryBlock.cpp:36
Memory::Range
Memory range.
Definition: Memory.h:55
StrictSingleton< FileDescriptor >::instance
static FileDescriptor * instance()
Retrieve the instance.
Definition: Singleton.h:53
ulong
unsigned long ulong
Unsigned long number.
Definition: Types.h:47
Types.h
errno
C int errno
The lvalue errno is used by many functions to return error values.
MemoryBlock::set
static void * set(void *dest, int ch, unsigned count)
Fill memory with a constant byte.
Definition: MemoryBlock.cpp:25
ExecutableFormat::Region::access
Memory::Access access
Definition: ExecutableFormat.h:61
string.h
ExecutableFormat::Region
Memory region.
Definition: ExecutableFormat.h:55
Memory::Writable
@ Writable
Definition: Memory.h:42
Memory::User
@ User
Definition: Memory.h:44
FileSystemClient
FileSystemClient provides a simple interface to a FileSystemServer.
Definition: FileSystemClient.h:42
PAGESIZE
#define PAGESIZE
ARM uses 4K pages.
Definition: ARMConstant.h:97
API::Write
@ Write
Definition: API.h:99
Resume
@ Resume
Definition: ProcessCtl.h:55
Address
unsigned long Address
A memory address.
Definition: Types.h:131
ExecutableFormat::regions
virtual Result regions(Region *regions, Size *count) const =0
Memory regions a program needs at runtime.
PATH_MAX
#define PATH_MAX
Maximum file path length.
Definition: limits.h:37
VMCtl
API::Result VMCtl(const ProcessID procID, const MemoryOperation op, Memory::Range *range=ZERO)
Prototype for user applications.
Definition: VMCtl.h:61
UnMap
@ UnMap
Definition: VMCtl.h:39
FileDescriptor.h
ProcessCtl
API::Result ProcessCtl(const ProcessID proc, const ProcessOperation op, const Address addr=0, const Address output=0)
Prototype for user applications.
Definition: ProcessCtl.h:93
Memory::Readable
@ Readable
Definition: Memory.h:41
ExecutableFormat::find
static Result find(const u8 *image, const Size size, ExecutableFormat **fmt)
Find a ExecutableFormat which can handle the given format.
Definition: ExecutableFormat.cpp:32
limits.h
ExecutableFormat::Region::dataOffset
Address dataOffset
Definition: ExecutableFormat.h:58
uint
unsigned int uint
Unsigned integer number.
Definition: Types.h:44
ExecutableFormat::Region::virt
Address virt
Definition: ExecutableFormat.h:57
FileSystemClient::getCurrentDirectory
const String * getCurrentDirectory() const
Get current directory String.
Definition: FileSystemClient.cpp:146
ARGV_SIZE
#define ARGV_SIZE
Maximum size of each argument.
Definition: Runtime.h:33
ARMMap
Memory mapping for the kernel and user processes on the ARM architecture.
Definition: ARMMap.h:37
SELF
#define SELF
Definition: ProcessID.h:35
ENOEXEC
#define ENOEXEC
Executable file format error.
Definition: errno.h:181
Memory::Range::phys
Address phys
Physical address.
Definition: Memory.h:58
MemoryMap::range
Memory::Range range(Region region) const
Get memory range for the given region.
Definition: MemoryMap.cpp:36
ExecutableFormat::Region::memorySize
Size memorySize
Definition: ExecutableFormat.h:60
Size
unsigned int Size
Any sane size indicator cannot go negative.
Definition: Types.h:128
EIO
#define EIO
I/O error.
Definition: errno.h:130
EFAULT
#define EFAULT
Bad address.
Definition: errno.h:106
pid_t
ProcessID pid_t
Used for process IDs and process group IDs.
Definition: types.h:32
unistd.h
ExecutableFormat::entry
virtual Result entry(Address *entry) const =0
Lookup the program entry point.
FileSystemClient.h
entry
u32 entry[]
Definition: IntelACPI.h:64
u8
unsigned char u8
Unsigned 8-bit number.
Definition: Types.h:59
spawn
int spawn(Address program, Size programSize, const char *argv[])
Create a new process using in-memory image.
Definition: spawn.cpp:29
ExecutableFormat::Region::dataSize
Size dataSize
Definition: ExecutableFormat.h:59
memset
void * memset(void *dest, int ch, size_t count)
Fill memory with a constant byte.
Definition: memset.cpp:20
API::Success
@ Success
Definition: API.h:70
ANY
#define ANY
Definition: ProcessID.h:34
Memory::Range::virt
Address virt
Virtual address.
Definition: Memory.h:57
MemoryMap::UserArgs
@ UserArgs
< Used for copying program arguments and file descriptors
Definition: MemoryMap.h:61
Spawn
@ Spawn
Definition: ProcessCtl.h:39
VMCopy
API::Result VMCopy(const ProcessID proc, const API::Operation how, const Address ours, const Address theirs, const Size sz)
Prototype for user applications.
Definition: VMCopy.h:42
Memory::Range::size
Size size
Size in number of bytes.
Definition: Memory.h:59
Memory::Range::access
Access access
Page access flags.
Definition: Memory.h:60
ZERO
#define ZERO
Zero value.
Definition: Macros.h:43
MapContiguous
@ MapContiguous
Definition: VMCtl.h:37
ExecutableFormat
Abstraction class of various executable formats.
Definition: ExecutableFormat.h:48
KillPID
@ KillPID
Definition: ProcessCtl.h:40
ExecutableFormat::Success
@ Success
Definition: ExecutableFormat.h:70
errno.h