1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
|
/* -------------------------------------------
Copyright (C) 2024-2025, Amlal El Mahrouss, licensed under the Apache 2.0 license.
Copyright (C) 2025, Amlal El Mahrouss & NeKernel contributors, licensed under the Apache 2.0 license.
------------------------------------------- */
#include <ArchKit/ArchKit.h>
#include <KernelKit/DebugOutput.h>
#include <KernelKit/HeapMgr.h>
#include <KernelKit/PEFCodeMgr.h>
#include <KernelKit/ProcessScheduler.h>
#include <NeKit/Defines.h>
#include <NeKit/KString.h>
#include <NeKit/KernelPanic.h>
#include <NeKit/OwnPtr.h>
#include <NeKit/Utils.h>
/// @author Amlal El Mahrouss (amlal@nekernel.org)
/// @brief PEF backend for the Code Manager.
/// @file PEFCodeMgr.cc
/// @brief PEF stack size symbol.
#define kPefStackSizeSymbol "__PEFSizeOfReserveStack"
#define kPefHeapSizeSymbol "__PEFSizeOfReserveHeap"
#define kPefNameSymbol "__PEFProgramName"
namespace Kernel {
namespace Detail {
/***********************************************************************************/
/// @brief Get the PEF platform signature according to the compiled architecture.
/***********************************************************************************/
static UInt32 ldr_get_platform(void) noexcept {
#if defined(__NE_32X0__)
return kPefArch32x0;
#elif defined(__NE_64X0__)
return kPefArch64x0;
#elif defined(__NE_AMD64__)
return kPefArchAMD64;
#elif defined(__NE_PPC64__)
return kPefArchPowerPC;
#elif defined(__NE_ARM64__)
return kPefArchARM64;
#else
return kPefArchInvalid;
#endif // __32x0__ || __64x0__ || __x86_64__
}
} // namespace Detail
/***********************************************************************************/
/// @brief PEF loader constructor w/ blob.
/// @param blob file blob.
/***********************************************************************************/
PEFLoader::PEFLoader(const VoidPtr blob) : fCachedBlob(blob) {
MUST_PASS(fCachedBlob);
if (!fCachedBlob) {
this->fBad = YES;
return;
}
PEFContainer* container = reinterpret_cast<PEFContainer*>(fCachedBlob);
if (container->Abi == kPefAbi &&
container->Count >=
3) { /* if same ABI, AND: .text, .bss, .data (or at least similar) exists */
if (container->Cpu == Detail::ldr_get_platform() && container->Magic[0] == kPefMagic[0] &&
container->Magic[1] == kPefMagic[1] && container->Magic[2] == kPefMagic[2] &&
container->Magic[3] == kPefMagic[3] && container->Magic[4] == kPefMagic[4]) {
return;
} else if (container->Magic[0] == kPefMagicFat[0] && container->Magic[1] == kPefMagicFat[1] &&
container->Magic[2] == kPefMagicFat[2] && container->Magic[3] == kPefMagicFat[3] &&
container->Magic[4] == kPefMagicFat[4]) {
/// This is a fat binary. Treat it as such.
this->fFatBinary = YES;
return;
}
}
kout << "PEFLoader: warning: Binary format error!\r";
this->fFatBinary = NO;
this->fBad = YES;
if (this->fCachedBlob) mm_free_ptr(this->fCachedBlob);
this->fCachedBlob = nullptr;
}
/***********************************************************************************/
/// @brief PEF loader constructor.
/// @param path the filesystem path.
/***********************************************************************************/
PEFLoader::PEFLoader(const Char* path) : fCachedBlob(nullptr), fFatBinary(false), fBad(false) {
fFile.New(const_cast<Char*>(path), kRestrictRB);
fPath = KStringBuilder::Construct(path).Leak();
constexpr auto kPefHeader = "PEF_CONTAINER";
/// @note zero here means that the FileMgr will read every container header inside the file.
fCachedBlob = fFile->Read(kPefHeader, 0UL);
if (!fCachedBlob) {
this->fBad = YES;
return;
}
PEFContainer* container = reinterpret_cast<PEFContainer*>(fCachedBlob);
if (container->Abi == kPefAbi &&
container->Count >=
3) { /* if same ABI, AND: .text, .bss, .data (or at least similar) exists */
if (container->Cpu == Detail::ldr_get_platform() && container->Magic[0] == kPefMagic[0] &&
container->Magic[1] == kPefMagic[1] && container->Magic[2] == kPefMagic[2] &&
container->Magic[3] == kPefMagic[3] && container->Magic[4] == kPefMagic[4]) {
return;
} else if (container->Magic[0] == kPefMagicFat[0] && container->Magic[1] == kPefMagicFat[1] &&
container->Magic[2] == kPefMagicFat[2] && container->Magic[3] == kPefMagicFat[3] &&
container->Magic[4] == kPefMagicFat[4]) {
/// This is a fat binary, treat it as such.
this->fFatBinary = YES;
return;
}
}
kout << "PEFLoader: warning: Binary format error!\r";
this->fFatBinary = NO;
this->fBad = YES;
if (this->fCachedBlob) mm_free_ptr(this->fCachedBlob);
this->fCachedBlob = nullptr;
}
/***********************************************************************************/
/// @brief PEF destructor.
/***********************************************************************************/
PEFLoader::~PEFLoader() {
if (fCachedBlob) mm_free_ptr(fCachedBlob);
fFile.Delete();
}
/***********************************************************************************/
/// @brief Finds the symbol according to it's name.
/// @param name name of symbol.
/// @param kind kind of symbol we want.
/***********************************************************************************/
ErrorOr<VoidPtr> PEFLoader::FindSymbol(const Char* name, Int32 kind) {
if (!fCachedBlob || fBad || !name) return ErrorOr<VoidPtr>{kErrorInvalidData};
auto blob = fFile->Read(name, sizeof(PEFCommandHeader));
if (!blob) return ErrorOr<VoidPtr>{kErrorInvalidData};
PEFContainer* container = reinterpret_cast<PEFContainer*>(fCachedBlob);
if (!container) return ErrorOr<VoidPtr>{kErrorInvalidData};
PEFCommandHeader* command_header = reinterpret_cast<PEFCommandHeader*>(blob);
if (command_header->VMSize < 1 || command_header->VMAddress == 0)
return ErrorOr<VoidPtr>{kErrorInvalidData};
/// fat binary check.
if (command_header->Cpu != container->Cpu && !this->fFatBinary)
return ErrorOr<VoidPtr>{kErrorInvalidData};
const auto kMangleCharacter = '$';
const Char* kContainerKinds[] = {".code64", ".data64", ".zero64", nullptr};
ErrorOr<KString> error_or_symbol;
switch (kind) {
case kPefCode: {
error_or_symbol = KStringBuilder::Construct(kContainerKinds[0]); // code symbol.
break;
}
case kPefData: {
error_or_symbol = KStringBuilder::Construct(kContainerKinds[1]); // data symbol.
break;
}
case kPefZero: {
error_or_symbol = KStringBuilder::Construct(kContainerKinds[2]); // block starting symbol.
break;
}
default:
return ErrorOr<VoidPtr>{kErrorInvalidData};
// prevent that from the kernel's mode perspective, let that happen if it
// were a user process.
}
Char* unconst_symbol = const_cast<Char*>(name);
for (SizeT i = 0UL; i < rt_string_len(unconst_symbol, kPefNameLen); ++i) {
if (rt_is_space(unconst_symbol[i])) {
unconst_symbol[i] = kMangleCharacter;
}
}
error_or_symbol.Leak().Leak() += name;
if (KStringBuilder::Equals(command_header->Name, error_or_symbol.Leak().Leak().CData())) {
if (command_header->Kind == kind) {
if (command_header->Cpu != Detail::ldr_get_platform()) {
if (!this->fFatBinary) {
mm_free_ptr(blob);
blob = nullptr;
return ErrorOr<VoidPtr>{kErrorInvalidData};
}
}
Char* container_blob_value = new Char[command_header->VMSize];
rt_copy_memory_safe((VoidPtr) ((Char*) blob + sizeof(PEFCommandHeader)), container_blob_value,
command_header->VMSize, command_header->VMSize);
mm_free_ptr(blob);
kout << "PEFLoader: info: Load stub: " << command_header->Name << "!\r";
Int32 ret = 0;
SizeT pages_count = (command_header->VMSize + kPageSize - 1) / kPageSize;
for (SizeT i_vm{}; i_vm < pages_count; ++i_vm) {
ret = HAL::mm_map_page(
(VoidPtr) (command_header->VMAddress + (i_vm * kPageSize)),
(VoidPtr) (HAL::mm_get_page_addr(container_blob_value) + (i_vm * kPageSize)),
HAL::kMMFlagsPresent | HAL::kMMFlagsUser);
if (ret != kErrorSuccess) {
/// We set the VMAddress to nullptr, if the mapping fail here.
for (SizeT i_vm_unmap{}; i_vm_unmap < i_vm; ++i_vm_unmap) {
ret = HAL::mm_map_page((VoidPtr) (command_header->VMAddress + (i_vm * kPageSize)),
nullptr, HAL::kMMFlagsPresent | HAL::kMMFlagsUser);
}
delete[] container_blob_value;
container_blob_value = nullptr;
return ErrorOr<VoidPtr>{kErrorInvalidData};
}
}
return ErrorOr<VoidPtr>{container_blob_value};
}
}
mm_free_ptr(blob);
return ErrorOr<VoidPtr>{kErrorInvalidData};
}
/// @brief Finds the executable entrypoint.
/// @return
ErrorOr<VoidPtr> PEFLoader::FindStart() {
if (auto sym = this->FindSymbol(kPefStart, kPefCode); sym) return sym;
return ErrorOr<VoidPtr>(kErrorExecutable);
}
/// @brief Tells if the executable is loaded or not.
/// @return Whether it's not bad and is cached.
bool PEFLoader::IsLoaded() noexcept {
return !fBad && fCachedBlob;
}
const Char* PEFLoader::Path() {
return fPath.Leak().CData();
}
const Char* PEFLoader::AsString() {
#ifdef __32x0__
return "32x0 PEF";
#elif defined(__64x0__)
return "64x0 PEF";
#elif defined(__x86_64__)
return "x86_64 PEF";
#elif defined(__aarch64__)
return "AARCH64 PEF";
#elif defined(__powerpc64__)
return "POWER64 PEF";
#else
return "???? PEF";
#endif // __32x0__ || __64x0__ || __x86_64__ || __powerpc64__
}
const Char* PEFLoader::MIME() {
return kPefApplicationMime;
}
ErrorOr<VoidPtr> PEFLoader::GetBlob() {
return ErrorOr<VoidPtr>{this->fCachedBlob};
}
namespace Utils {
ProcessID rtl_create_user_process(PEFLoader& exec, const Int32& process_kind) noexcept {
auto errOrStart = exec.FindStart();
if (errOrStart.Error() != kErrorSuccess) return kSchedInvalidPID;
auto symname = exec.FindSymbol(kPefNameSymbol, kPefData);
if (!symname) {
symname = ErrorOr<VoidPtr>{(VoidPtr) rt_alloc_string("USER_PROCESS_PEF")};
}
ProcessID id =
UserProcessScheduler::The().Spawn(reinterpret_cast<const Char*>(symname.Leak().Leak()),
errOrStart.Leak().Leak(), exec.GetBlob().Leak().Leak());
mm_free_ptr(symname.Leak().Leak());
if (id != kSchedInvalidPID) {
auto stacksym = exec.FindSymbol(kPefStackSizeSymbol, kPefData);
if (!stacksym) {
stacksym = ErrorOr<VoidPtr>{(VoidPtr) new UIntPtr(kSchedMaxStackSz)};
}
if ((*(volatile UIntPtr*) stacksym.Leak().Leak()) > kSchedMaxStackSz) {
*(volatile UIntPtr*) stacksym.Leak().Leak() = kSchedMaxStackSz;
}
UserProcessScheduler::The().TheCurrentTeam().AsArray()[id].Kind = process_kind;
UserProcessScheduler::The().TheCurrentTeam().AsArray()[id].StackSize =
*(UIntPtr*) stacksym.Leak().Leak();
mm_free_ptr(stacksym.Leak().Leak());
}
return id;
}
} // namespace Utils
} // namespace Kernel
|