FreeNOS
RecoveryServer.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2020 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 <Log.h>
20#include <FileSystemClient.h>
21#include <ExecutableFormat.h>
22#include <Lz4Decompressor.h>
23#include "RecoveryServer.h"
24
30
32{
33 DEBUG("pid = " << msg->pid);
34
35 // The identifier must be in range of allowed values
36 if (msg->pid >= MAX_PROCS)
37 {
38 ERROR("invalid PID " << msg->pid);
40 return;
41 }
42
43 // Retrieve state information of the given process
44 ProcessInfo pinfo;
45 API::Result result = ProcessCtl(msg->pid, InfoPID, (Address) &pinfo);
46 if (result != API::Success)
47 {
48 ERROR("failed to retrieve ProcessInfo for PID " << msg->pid <<
49 ": result = " << (int)result);
51 return;
52 }
53
54 // Halt the process. Brings it out of the scheduler
55 // and also prevents other processes from waking it up while
56 // it is being restarted by us.
57 result = ProcessCtl(msg->pid, Stop);
58 if (result != API::Success)
59 {
60 ERROR("failed to stop PID " << msg->pid <<
61 ": result = " << (int) result);
63 return;
64 }
65
66 // Retrieve full command string
67 char cmd[128];
68 const Arch::MemoryMap map;
69 const Memory::Range argRange = map.range(MemoryMap::UserArgs);
70
71 result = VMCopy(msg->pid, API::Read, (Address) cmd, argRange.virt, sizeof(cmd));
72 if (result != API::Success)
73 {
74 ERROR("failed to read command string from PID " << msg->pid <<
75 ": result = " << (int) result);
77 return;
78 }
79 cmd[sizeof(cmd) - 1] = 0;
80
81 // Fetch program path from command string
82 List<String> progpath = String(cmd).split(' ');
83 if (progpath.count() == 0)
84 {
85 ERROR("failed to find program path for PID " << msg->pid);
87 return;
88 }
89
90 // Write fresh copy of the program inside
91 if (!reloadProgram(msg->pid, *progpath[0]))
92 {
93 ERROR("failed to reload PID " << msg->pid <<
94 " from " << *progpath[0] <<
95 ": result = " << (int) result);
97 return;
98 }
99
100 // Continue program
101 result = ProcessCtl(msg->pid, Resume);
102 if (result != API::Success)
103 {
104 ERROR("failed to resume PID " << msg->pid <<
105 ": result = " << (int) result);
107 return;
108 }
109
110 // Success
112}
113
115 const char *path) const
116{
117 const FileSystemClient fs;
119 Size fd;
120
121 DEBUG("pid = " << pid << " path = " << path);
122
123 // Retrieve file information
124 const FileSystem::Result statResult = fs.statFile(path, &st);
125 if (statResult != FileSystem::Success)
126 {
127 ERROR("failed to statFile() for " << path << ": result = " << (int) statResult);
128 return false;
129 }
130
131 // Map memory buffer for the compressed program image
132 Memory::Range compressed;
133 compressed.virt = ZERO;
134 compressed.phys = ZERO;
135 compressed.size = st.size;
137
138 // Create memory mapping
139 API::Result mapResult = VMCtl(SELF, MapContiguous, &compressed);
140 if (mapResult != API::Success)
141 {
142 ERROR("failed to map compressed memory: result = " << (int) mapResult);
143 return false;
144 }
145
146 // Open file
147 const FileSystem::Result openResult = fs.openFile(path, fd);
148 if (openResult != FileSystem::Success)
149 {
150 ERROR("failed to openFile() for " << path <<
151 ": result = " << (int) openResult);
152 VMCtl(SELF, Release, &compressed);
153 return false;
154 }
155
156 // Read the program image
157 Size num = st.size;
158 const FileSystem::Result readResult = fs.readFile(fd, (void *) compressed.virt, &num);
159 if (readResult != FileSystem::Success || num != st.size)
160 {
161 ERROR("failed to readFile() for " << path <<
162 ": result = " << (int) readResult << ", num = " << num);
163 VMCtl(SELF, Release, &compressed);
164 return false;
165 }
166
167 // Close file
168 const FileSystem::Result closeResult = fs.closeFile(fd);
169 if (closeResult != FileSystem::Success)
170 {
171 ERROR("failed to closeFile() for " << path <<
172 ": result = " << (int) closeResult);
173 VMCtl(SELF, Release, &compressed);
174 return false;
175 }
176
177 // Initialize decompressor
178 Lz4Decompressor lz4((const void *) compressed.virt, st.size);
179 Lz4Decompressor::Result lz4Result = lz4.initialize();
180 if (lz4Result != Lz4Decompressor::Success)
181 {
182 ERROR("failed to initialize LZ4 decompressor: result = " << (int) lz4Result);
183 VMCtl(SELF, Release, &compressed);
184 return false;
185 }
186
187 // Map memory buffer for the uncompressed program image
188 Memory::Range uncompressed;
189 uncompressed.virt = ZERO;
190 uncompressed.phys = ZERO;
191 uncompressed.size = lz4.getUncompressedSize();
193
194 // Create memory mapping
195 mapResult = VMCtl(SELF, MapContiguous, &uncompressed);
196 if (mapResult != API::Success)
197 {
198 ERROR("failed to map uncompressed memory: result = " << (int) mapResult);
199 VMCtl(SELF, Release, &compressed);
200 return false;
201 }
202
203 // Decompress entire file
204 lz4Result = lz4.read((void *)uncompressed.virt, lz4.getUncompressedSize());
205 if (lz4Result != Lz4Decompressor::Success)
206 {
207 ERROR("failed to decompress file: result = " << (int) lz4Result);
208 VMCtl(SELF, Release, &compressed);
209 VMCtl(SELF, Release, &uncompressed);
210 return false;
211 }
212
213 // Release current memory pages
214 if (!cleanupProgram(pid))
215 {
216 ERROR("failed to cleanup program data for PID " << pid);
217 VMCtl(SELF, Release, &compressed);
218 VMCtl(SELF, Release, &uncompressed);
219 return false;
220 }
221
222 // Write to program
223 if (!rewriteProgram(pid, uncompressed.virt, num))
224 {
225 ERROR("failed to reset data for PID " << pid);
226 VMCtl(SELF, Release, &compressed);
227 VMCtl(SELF, Release, &uncompressed);
228 return false;
229 }
230
231 // Cleanup program buffers
232 API::Result releaseResult = VMCtl(SELF, Release, &compressed);
233 if (releaseResult != API::Success)
234 {
235 DEBUG("failed to release compressed memory: result = " << (int) releaseResult);
236 VMCtl(SELF, Release, &uncompressed);
237 return false;
238 }
239
240 releaseResult = VMCtl(SELF, Release, &uncompressed);
241 if (releaseResult != API::Success)
242 {
243 DEBUG("failed to release uncompressed memory: result = " << (int) releaseResult);
244 return false;
245 }
246
247 // Success
248 return true;
249}
250
252{
253 const Arch::MemoryMap map;
254 Memory::Range range;
255
256 DEBUG("pid = " << pid);
257
258 range = map.range(MemoryMap::UserData);
259 const API::Result dataResult = VMCtl(pid, ReleaseSections, &range);
260 if (dataResult != API::Success)
261 {
262 ERROR("failed to release UserData region in PID " << pid <<
263 ": result = " << (int) dataResult);
264 return false;
265 }
266
267 range = map.range(MemoryMap::UserHeap);
268 const API::Result heapResult = VMCtl(pid, ReleaseSections, &range);
269 if (heapResult != API::Success)
270 {
271 ERROR("failed to release UserHeap region in PID " << pid <<
272 ": result = " << (int) heapResult);
273 return false;
274 }
275
276 range = map.range(MemoryMap::UserPrivate);
277 const API::Result privResult = VMCtl(pid, ReleaseSections, &range);
278 if (privResult != API::Success)
279 {
280 ERROR("failed to release UserPrivate region in PID " << pid <<
281 ": result = " << (int) privResult);
282 return false;
283 }
284
285 return true;
286}
287
289 const Address program,
290 const Size size) const
291{
292 ExecutableFormat *fmt;
293 ExecutableFormat::Region regions[16];
294 Arch::MemoryMap map;
295 Memory::Range range;
296 Size numRegions = 16;
297 Address entry = 0;
298
299 DEBUG("pid = " << pid << " program = " << (void *) program << " size = " << size);
300
301 // Attempt to read executable format
302 const ExecutableFormat::Result execResult =
303 ExecutableFormat::find((u8 *) program, size, &fmt);
304
305 if (execResult != ExecutableFormat::Success)
306 {
307 ERROR("failed to parse executable: result = " << (int) execResult);
308 return false;
309 }
310
311 // Find entry point
312 const ExecutableFormat::Result entryResult = fmt->entry(&entry);
313 if (entryResult != ExecutableFormat::Success)
314 {
315 ERROR("failed to retrieve entry point: result = " << (int) entryResult);
316 delete fmt;
317 return false;
318 }
319
320 // Retrieve memory regions
321 const ExecutableFormat::Result regionResult = fmt->regions(regions, &numRegions);
322 if (regionResult != ExecutableFormat::Success)
323 {
324 ERROR("failed to retrieve regions: result = " << (int) regionResult);
325 delete fmt;
326 return false;
327 }
328
329 // Release buffers
330 delete fmt;
331
332 // Map program regions into virtual memory of the new process
333 for (Size i = 0; i < numRegions; i++)
334 {
335 // Setup memory range to copy region data
336 range.virt = regions[i].virt;
337 range.phys = ZERO;
338 range.size = regions[i].memorySize;
339 range.access = regions[i].access;
340
341 // Create mapping in the process
342 const API::Result mapResult = VMCtl(pid, MapContiguous, &range);
343 if (mapResult != API::Success)
344 {
345 ERROR("failed to map " << (void *) regions[i].virt <<
346 " in PID " << pid << ": result = " << (int) mapResult);
347 return false;
348 }
349
350 // Map inside our process
351 range.virt = ZERO;
352 const API::Result selfResult = VMCtl(SELF, MapContiguous, &range);
353 if (selfResult != API::Success)
354 {
355 ERROR("failed to map " << (void *) regions[i].virt <<
356 ": result = " << (int) selfResult);
357 return false;
358 }
359
360 // Copy data bytes
361 MemoryBlock::copy((void *)range.virt, (const void *)(program + regions[i].dataOffset),
362 regions[i].dataSize);
363
364 // Nulify remaining space
365 if (regions[i].memorySize > regions[i].dataSize)
366 {
367 MemoryBlock::set((void *)(range.virt + regions[i].dataSize), 0,
368 regions[i].memorySize - regions[i].dataSize);
369 }
370
371 // Remove temporary mapping
372 const API::Result unmapResult = VMCtl(SELF, UnMap, &range);
373 if (unmapResult != API::Success)
374 {
375 ERROR("failed to unmap " << (void *) regions[i].virt <<
376 ": result = " << (int) unmapResult);
377 return false;
378 }
379 }
380
381 // Reset program registers
382 const API::Result resetResult = ProcessCtl(pid, Reset, entry);
383 if (resetResult != API::Success)
384 {
385 ERROR("failed to reset PID " << pid <<
386 ": result = " << (int) resetResult);
387 return false;
388 }
389
390 // Success
391 return true;
392}
u32 entry[]
Definition IntelACPI.h:1
Result
Enumeration of generic kernel API result codes.
Definition API.h:69
@ Success
Definition API.h:70
@ Read
Definition API.h:98
Memory mapping for the kernel and user processes on the ARM architecture.
Definition ARMMap.h:38
Template class which serves incoming messages from Channels using MessageHandlers.
void addIPCHandler(const Size slot, IPCHandlerFunction h, const bool sendReply=true)
Register a new IPC message action handler.
Abstraction class of various executable formats.
virtual Result regions(Region *regions, Size *count) const =0
Memory regions a program needs at runtime.
static Result find(const u8 *image, const Size size, ExecutableFormat **fmt)
Find a ExecutableFormat which can handle the given format.
virtual Result entry(Address *entry) const =0
Lookup the program entry point.
FileSystemClient provides a simple interface to a FileSystemServer.
FileSystem::Result closeFile(const Size descriptor) const
Close a file.
FileSystem::Result statFile(const char *path, FileSystem::FileStat *st) const
Retrieve status of a file.
FileSystem::Result readFile(const Size descriptor, void *buf, Size *size) const
Read a file.
FileSystem::Result openFile(const char *path, Size &descriptor) const
Open a file.
Simple linked list template class.
Definition List.h:37
Decompress data using the LZ4 algorithm created by Yann Collet.
Result initialize()
Initialize the decompressor.
u64 getUncompressedSize() const
Get size of the uncompressed data.
Result
Result codes.
Result read(void *buffer, const Size size) const
Reads compressed data.
static void * set(void *dest, int ch, unsigned count)
Fill memory with a constant byte.
static Size copy(void *dest, const void *src, Size count)
Copy memory from one place to another.
@ UserData
< User program data from libexec, e.g.
Definition MemoryMap.h:56
@ UserArgs
< Used for copying program arguments and file descriptors
Definition MemoryMap.h:61
@ UserPrivate
< User private dynamic memory mappings
Definition MemoryMap.h:59
@ UserHeap
< User heap
Definition MemoryMap.h:57
Memory::Range range(Region region) const
Get memory range for the given region.
Definition MemoryMap.cpp:36
Recovery Server.
bool rewriteProgram(const ProcessID pid, const Address program, const Size size) const
Overwrite process with given program data.
void restartProcess(RecoveryMessage *msg)
Restart a process.
bool cleanupProgram(const ProcessID pid) const
Release and unmap program data.
RecoveryServer()
Class constructor function.
bool reloadProgram(const ProcessID pid, const char *path) const
Overwrite the given process by fetching a fresh program data copy.
Abstraction of strings.
Definition String.h:42
List< String > split(const char delimiter) const
Split the String into parts separated by a delimiter.
Definition String.cpp:408
#define MAX_PROCS
Maximum number of processes.
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
#define SELF
Definition ProcessID.h:35
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
API::Result VMCtl(const ProcessID procID, const MemoryOperation op, Memory::Range *range=ZERO)
Prototype for user applications.
Definition VMCtl.h:61
@ ReleaseSections
Definition VMCtl.h:41
@ Release
Definition VMCtl.h:40
@ UnMap
Definition VMCtl.h:39
@ MapContiguous
Definition VMCtl.h:37
@ Resume
Definition ProcessCtl.h:55
@ InfoPID
Definition ProcessCtl.h:47
@ Reset
Definition ProcessCtl.h:56
@ Stop
Definition ProcessCtl.h:54
u32 ProcessID
Process Identification Number.
Definition Types.h:140
unsigned long Address
A memory address.
Definition Types.h:131
#define ERROR(msg)
Output an error message.
Definition Log.h:61
unsigned int Size
Any sane size indicator cannot go negative.
Definition Types.h:128
#define ZERO
Zero value.
Definition Macros.h:43
#define DEBUG(msg)
Output a debug message to standard output.
Definition Log.h:89
unsigned char u8
Unsigned 8-bit number.
Definition Types.h:59
Result
Result code for filesystem Actions.
Definition FileSystem.h:53
@ User
Definition Memory.h:44
@ Readable
Definition Memory.h:41
@ Writable
Definition Memory.h:42
@ NotFound
Definition Recovery.h:47
@ IOError
Definition Recovery.h:45
@ Success
Definition Recovery.h:44
@ InvalidArgument
Definition Recovery.h:46
@ RestartProcess
Definition Recovery.h:36
Contains file information.
Definition FileSystem.h:114
Size size
< File access permission bits.
Definition FileSystem.h:119
Memory range.
Definition Memory.h:56
Size size
Size in number of bytes.
Definition Memory.h:59
Address phys
Physical address.
Definition Memory.h:58
Address virt
Virtual address.
Definition Memory.h:57
Access access
Page access flags.
Definition Memory.h:60
Process information structure, used for Info.
Definition ProcessCtl.h:64
Recovery IPC message.
ProcessID pid
Process identifier of target process.
Recovery::Result result
Result of action.