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
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright (C) 2026 Richard Qin */
/*
* exec.cc — ELF64 binary loader and execve implementation
*
* Loads a static ELF64 executable into a fresh address space,
* constructs the user stack (argc/argv/envp/auxv), and redirects
* the current task's trapframe to begin executing at the ELF entry.
*
* Reference: Linux fs/binfmt_elf.c, fs/exec.c
*/
#include <lume/elf.h>
#include <lume/types.h>
#include <lume/task.h>
#include <lume/vmm.h>
#include <lume/addr.h>
#include <lume/config.h>
#include <lume/errno.h>
#include <lume/pmm.h>
#include <lume/kprintf.h>
#include <lume/klog.h>
#include <lume/file.h>
#include <lume/fs.h>
#include <lume/dcache.h>
#include <lume/namei.h>
#include <lume/fdtable.h>
#include <lume/mm_layout.h>
#include <lume/signal_types.h>
#include <arch/pmap.h>
#include <arch/trapframe.h>
#include <arch/mmu.h>
#include <arch/cpu.h>
#include <arch/trap.h>
/* Assembly entry points (trap_vector.S) */
extern "C" void trap_vector();
extern "C" [[noreturn]] void trap_dispatch(arch::TrapFrame*);
extern "C" void userret(arch::TrapFrame* tf);
/* ================================================================== */
/* Constants */
/* ================================================================== */
/* User stack layout comes from mm_layout.h (MI layer).
* kUserStackTop, kUserStackBase, kUserStackSize are defined there,
* derived from kUserVaLimit so they automatically adapt to the
* page table mode (SV39/SV48). */
/* ================================================================== */
/* Helper: kernel_read — read from a file into a kernel buffer */
/* ================================================================== */
static int64 kernel_read(file* fp, void* buf, uint64 count, int64 offset)
{
int64 pos = offset;
if (!fp->f_op || !fp->f_op->read) return -EINVAL;
return fp->f_op->read(fp, static_cast<char*>(buf), count, &pos);
}
/* ================================================================== */
/* Helper: copy data to a user page via kernel direct map */
/* ================================================================== */
static void memcpy_kernel(void* dst, const void* src, uint64 n)
{
auto* d = static_cast<uint8*>(dst);
auto* s = static_cast<const uint8*>(src);
for (uint64 i = 0; i < n; i++)
d[i] = s[i];
}
static void memset_kernel(void* dst, uint8 val, uint64 n)
{
auto* d = static_cast<uint8*>(dst);
for (uint64 i = 0; i < n; i++)
d[i] = val;
}
static uint64 strlen_kernel(const char* s)
{
uint64 n = 0;
while (s[n]) n++;
return n;
}
/* ================================================================== */
/* Helper: allocate and map a page at a specific user VA */
/* ================================================================== */
static int map_page_at(VmSpace* mm, uint64 va, uint64 pte_perm)
{
Page* pg = pmm_alloc_page();
if (!pg) return -ENOMEM;
uint64 pa = page_to_pa(pg).raw;
/* Zero the page */
void* kva = reinterpret_cast<void*>(pa_to_va(PhysAddr{pa}).raw);
memset_kernel(kva, 0, kPageSize);
int ret = pmap::map(mm->root_pa, va, pa, pte_perm);
if (ret != 0) {
pmm_free_page(pg);
return -ENOMEM;
}
page_inc_mapcount(pg);
return 0;
}
/* ================================================================== */
/* load_elf_binary — parse ELF and map segments */
/* ================================================================== */
struct elf_load_info {
uint64 entry; /* ELF entry point (after base offset) */
uint64 phdr_addr; /* User VA where phdr was loaded */
uint16 phnum; /* Number of program headers */
uint16 phentsize; /* Size of each phdr */
uint64 brk_start; /* End of highest PT_LOAD segment (for brk) */
uint64 load_bias; /* PIE load bias (0 for ET_EXEC) */
};
static int load_elf_binary(file* fp, VmSpace* mm, elf_load_info* info)
{
/* Read ELF header */
Elf64_Ehdr ehdr{};
int64 n = kernel_read(fp, &ehdr, sizeof(ehdr), 0);
if (n < static_cast<int64>(sizeof(ehdr))) {
printk(KERN_ERR "exec: failed to read ELF header (got %lld)\n", n);
return -ENOEXEC;
}
/* Validate magic */
if (ehdr.e_ident[EI_MAG0] != ELFMAG0 ||
ehdr.e_ident[EI_MAG1] != ELFMAG1 ||
ehdr.e_ident[EI_MAG2] != ELFMAG2 ||
ehdr.e_ident[EI_MAG3] != ELFMAG3) {
printk(KERN_ERR "exec: bad ELF magic\n");
return -ENOEXEC;
}
/* Validate class (64-bit) and endianness (little-endian) */
if (ehdr.e_ident[EI_CLASS] != ELFCLASS64) return -ENOEXEC;
if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) return -ENOEXEC;
/* Validate type and machine */
if (ehdr.e_type != ET_EXEC && ehdr.e_type != ET_DYN) return -ENOEXEC;
if (ehdr.e_machine != EM_RISCV) return -ENOEXEC;
if (ehdr.e_phnum == 0 || ehdr.e_phentsize < sizeof(Elf64_Phdr))
return -ENOEXEC;
/* Read program headers */
uint64 phdr_total = static_cast<uint64>(ehdr.e_phnum) * ehdr.e_phentsize;
if (phdr_total > kPageSize) return -ENOEXEC; /* Sanity limit */
/* Allocate kernel buffer for program headers */
uint8 phdr_buf[512]; /* Max ~8 phdrs * 56 bytes each */
if (phdr_total > sizeof(phdr_buf)) return -ENOEXEC;
n = kernel_read(fp, phdr_buf, phdr_total, static_cast<int64>(ehdr.e_phoff));
if (n < static_cast<int64>(phdr_total)) return -ENOEXEC;
info->phnum = ehdr.e_phnum;
info->phentsize = ehdr.e_phentsize;
info->phdr_addr = 0;
info->brk_start = 0;
info->load_bias = 0;
/*
* PIE (ET_DYN): segments have p_vaddr starting near 0.
* We must choose a load base and add it to all addresses.
* Linux uses ELF_ET_DYN_BASE (~2/3 of TASK_SIZE); we use 0x400000.
*/
constexpr uint64 kPieLoadBase = 0x400000;
uint64 load_bias = (ehdr.e_type == ET_DYN) ? kPieLoadBase : 0;
info->load_bias = load_bias;
info->entry = ehdr.e_entry + load_bias;
/* Process each PT_LOAD segment */
for (uint16 i = 0; i < ehdr.e_phnum; i++) {
auto* phdr = reinterpret_cast<Elf64_Phdr*>(
phdr_buf + i * ehdr.e_phentsize);
if (phdr->p_type == PT_PHDR) {
/* Remember where program headers are mapped */
info->phdr_addr = phdr->p_vaddr + load_bias;
continue;
}
if (phdr->p_type != PT_LOAD) continue;
if (phdr->p_memsz == 0) continue;
/* Compute permissions */
uint64 perm = pmap::PTE_V | pmap::PTE_U;
if (phdr->p_flags & PF_R) perm |= pmap::PTE_R;
if (phdr->p_flags & PF_W) perm |= pmap::PTE_W;
if (phdr->p_flags & PF_X) perm |= pmap::PTE_X;
/* Page-align the segment boundaries (apply load bias for PIE) */
uint64 seg_start = page_align_down(phdr->p_vaddr + load_bias);
uint64 seg_end = page_align_up(phdr->p_vaddr + phdr->p_memsz + load_bias);
/* Create VMA for this segment */
uint64 vm_perm = VM_USER;
if (phdr->p_flags & PF_R) vm_perm |= VM_READ;
if (phdr->p_flags & PF_W) vm_perm |= VM_WRITE;
if (phdr->p_flags & PF_X) vm_perm |= VM_EXEC;
int err = vmm_map_user(mm, seg_start, seg_end - seg_start, vm_perm);
if (err) {
printk(KERN_ERR "exec: vmm_map_user failed for segment %d: %d\n",
i, err);
return err;
}
/* Map and fill pages */
for (uint64 va = seg_start; va < seg_end; va += kPageSize) {
err = map_page_at(mm, va, perm);
if (err) return err;
/* Determine how much file data goes into this page */
uint64 file_seg_start = phdr->p_vaddr + load_bias;
uint64 file_seg_end = phdr->p_vaddr + phdr->p_filesz + load_bias;
uint64 page_start = va;
uint64 page_end = va + kPageSize;
/* Overlap between [page_start, page_end) and [file_seg_start, file_seg_end) */
if (page_start < file_seg_end && page_end > file_seg_start) {
uint64 copy_start = (page_start > file_seg_start) ? page_start : file_seg_start;
uint64 copy_end = (page_end < file_seg_end) ? page_end : file_seg_end;
uint64 copy_len = copy_end - copy_start;
/* File offset for this data */
uint64 file_off = phdr->p_offset + (copy_start - phdr->p_vaddr - load_bias);
/* Get kernel VA of the mapped page */
uint64 pa;
if (!pmap::lookup(mm->root_pa, va, &pa)) return -EFAULT;
uint8* kva = reinterpret_cast<uint8*>(pa_to_va(PhysAddr{pa}).raw);
/* Read file data into the page */
uint64 page_offset = copy_start - va;
n = kernel_read(fp, kva + page_offset, copy_len,
static_cast<int64>(file_off));
if (n < static_cast<int64>(copy_len)) {
printk(KERN_ERR "exec: short read at offset %llu\n", file_off);
return -EIO;
}
}
/* BSS region (memsz > filesz) is already zeroed by map_page_at */
}
/* Track highest loaded address for brk */
if (seg_end > info->brk_start)
info->brk_start = seg_end;
/* Detect which PT_LOAD contains the program header table.
* Linux: fs/binfmt_elf.c line ~1209 */
if (info->phdr_addr == 0 &&
phdr->p_offset <= ehdr.e_phoff &&
ehdr.e_phoff < phdr->p_offset + phdr->p_filesz) {
info->phdr_addr = ehdr.e_phoff - phdr->p_offset
+ phdr->p_vaddr + load_bias;
}
}
/* phdr_addr should have been set by the PT_LOAD loop above
* or by a PT_PHDR entry. If still zero, something is wrong. */
if (info->phdr_addr == 0) {
printk(KERN_ERR "exec: unable to determine AT_PHDR address\n");
return -ENOEXEC;
}
return 0;
}
/* ================================================================== */
/* setup_user_stack — build argc/argv/envp/auxv on the user stack */
/* ================================================================== */
static int setup_user_stack(VmSpace* mm, const char* const* argv,
const char* const* envp,
const elf_load_info* info,
uint64* out_sp)
{
/* Create stack VMA */
uint64 vm_perm = VM_USER | VM_READ | VM_WRITE;
int err = vmm_map_user(mm, kUserStackBase, kUserStackSize, vm_perm);
if (err) return err;
/* We only need the top few pages for initial stack content.
* Map 2 pages at the top for argc/argv/envp/auxv. */
uint64 perm = pmap::PTE_V | pmap::PTE_U | pmap::PTE_R | pmap::PTE_W;
for (uint64 va = kUserStackTop - 2 * kPageSize; va < kUserStackTop; va += kPageSize) {
err = map_page_at(mm, va, perm);
if (err) return err;
}
/* Count arguments and environment */
int argc = 0;
int envc = 0;
if (argv) { while (argv[argc]) argc++; }
if (envp) { while (envp[envc]) envc++; }
/*
* Build the stack from top down using kernel VA access.
* Layout (top to bottom):
* 1. String data (argv strings, envp strings)
* 2. Padding (16-byte align)
* 3. auxv[]
* 4. NULL (end of envp)
* 5. envp[] pointers
* 6. NULL (end of argv)
* 7. argv[] pointers
* 8. argc
*/
/* First pass: calculate total string space */
uint64 str_total = 0;
for (int i = 0; i < argc; i++)
str_total += strlen_kernel(argv[i]) + 1;
for (int i = 0; i < envc; i++)
str_total += strlen_kernel(envp[i]) + 1;
/* String area starts at top of stack */
uint64 str_pos = kUserStackTop - str_total;
str_pos &= ~15ULL; /* 16-byte align */
/* Copy strings and record user addresses */
uint64 argv_addrs[32]; /* Max 32 args */
uint64 envp_addrs[32];
if (argc > 32) argc = 32;
if (envc > 32) envc = 32;
uint64 cur = str_pos;
for (int i = 0; i < argc; i++) {
uint64 len = strlen_kernel(argv[i]) + 1;
argv_addrs[i] = cur;
/* Copy string bytes to user stack via kernel VA */
uint64 pa;
if (!pmap::lookup(mm->root_pa, page_align_down(cur), &pa))
return -EFAULT;
uint8* kva = reinterpret_cast<uint8*>(pa_to_va(PhysAddr{pa}).raw);
uint64 off = cur - page_align_down(cur);
memcpy_kernel(kva + off, argv[i], len);
cur += len;
}
for (int i = 0; i < envc; i++) {
uint64 len = strlen_kernel(envp[i]) + 1;
envp_addrs[i] = cur;
uint64 pa;
if (!pmap::lookup(mm->root_pa, page_align_down(cur), &pa))
return -EFAULT;
uint8* kva = reinterpret_cast<uint8*>(pa_to_va(PhysAddr{pa}).raw);
uint64 off = cur - page_align_down(cur);
memcpy_kernel(kva + off, envp[i], len);
cur += len;
}
/* Build auxv, envp[], argv[], argc below the strings */
/* Count entries: argc + argv_ptrs + NULL + envp_ptrs + NULL + auxv (6 pairs + terminator) */
int auxv_count = 8; /* AT_PHDR, AT_PHENT, AT_PHNUM, AT_PAGESZ, AT_BASE, AT_ENTRY, AT_RANDOM, AT_NULL */
uint64 table_size = sizeof(uint64) * (1 + argc + 1 + envc + 1 + auxv_count * 2);
uint64 table_start = str_pos - table_size;
table_start &= ~15ULL;
/* Write the table entries.
* We need to handle page boundaries, but for simplicity we assume
* the table fits in the already-mapped pages. */
uint64 sp = table_start;
/* Helper: write a uint64 to user stack */
auto write_u64 = [&](uint64 addr, uint64 val) -> int {
uint64 pa;
if (!pmap::lookup(mm->root_pa, page_align_down(addr), &pa))
return -EFAULT;
uint8* kva = reinterpret_cast<uint8*>(pa_to_va(PhysAddr{pa}).raw);
uint64 off = addr - page_align_down(addr);
*reinterpret_cast<uint64*>(kva + off) = val;
return 0;
};
/* argc */
err = write_u64(sp, static_cast<uint64>(argc));
if (err) return err;
sp += 8;
/* argv[] */
for (int i = 0; i < argc; i++) {
err = write_u64(sp, argv_addrs[i]);
if (err) return err;
sp += 8;
}
err = write_u64(sp, 0); /* NULL terminator */
if (err) return err;
sp += 8;
/* envp[] */
for (int i = 0; i < envc; i++) {
err = write_u64(sp, envp_addrs[i]);
if (err) return err;
sp += 8;
}
err = write_u64(sp, 0); /* NULL terminator */
if (err) return err;
sp += 8;
/* auxv[] */
auto write_auxv = [&](uint64 type, uint64 val) -> int {
int r = write_u64(sp, type);
if (r) return r;
sp += 8;
r = write_u64(sp, val);
if (r) return r;
sp += 8;
return 0;
};
write_auxv(AT_PHDR, info->phdr_addr);
write_auxv(AT_PHENT, info->phentsize);
write_auxv(AT_PHNUM, info->phnum);
write_auxv(AT_PAGESZ, kPageSize);
write_auxv(AT_BASE, info->load_bias);
write_auxv(AT_ENTRY, info->entry);
write_auxv(AT_RANDOM, str_pos - 16); /* Point to 16 bytes of zeros for now */
write_auxv(AT_NULL, 0);
*out_sp = table_start;
return 0;
}
/* ================================================================== */
/* do_execve — main entry point */
/* ================================================================== */
int do_execve(const char* pathname, const char* const* argv,
const char* const* envp)
{
TaskControlBlock* curr = current_task();
if (!curr) return -EINVAL;
/* 1. Look up the file */
path p{};
int err = filename_lookup(AT_FDCWD, pathname, 0, &p);
if (err) {
printk(KERN_ERR "exec: file not found: %s (err=%d)\n", pathname, err);
return err;
}
inode* ip = p.de->d_inode;
if (!ip || !S_ISREG(ip->i_mode)) {
printk(KERN_ERR "exec: not a regular file: %s\n", pathname);
return -EACCES;
}
/* Open a kernel file handle */
file* fp = alloc_empty_file(O_RDONLY);
if (!fp) {
return -ENOMEM;
}
fp->f_inode = ip;
fp->f_path = p;
fp->f_op = ip->i_fop;
fp->f_pos = 0;
if (fp->f_op && fp->f_op->open) {
err = fp->f_op->open(ip, fp);
if (err) {
fput(fp);
return err;
}
}
/* 2. Create new address space */
VmSpace* new_mm = VmSpace::create();
if (!new_mm) {
fput(fp);
return -ENOMEM;
}
/* 3. Load ELF segments */
elf_load_info info{};
err = load_elf_binary(fp, new_mm, &info);
if (err) {
printk(KERN_ERR "exec: load_elf_binary failed: %d\n", err);
new_mm->put();
fput(fp);
dput(p.de);
return err;
}
/* 4. Set up user stack */
uint64 user_sp = 0;
err = setup_user_stack(new_mm, argv, envp, &info, &user_sp);
if (err) {
printk(KERN_ERR "exec: setup_user_stack failed: %d\n", err);
new_mm->put();
fput(fp);
return err;
}
/* Set brk */
new_mm->brk_start = page_align_up(info.brk_start);
new_mm->brk_end = new_mm->brk_start;
/* Set mmap base — Linux: arch_pick_mmap_layout() sets mm->mmap_base
* to TASK_UNMAPPED_BASE = PAGE_ALIGN(TASK_SIZE / 3).
* This ensures mmap(NULL) returns addresses well above the ELF
* text/data segments. */
new_mm->mmap_base = page_align_up(kUserVaLimit / 3);
fput(fp);
/* =============================================================== */
/* Point of no return — replace the current process */
/* =============================================================== */
/* 5a. Replace address space */
VmSpace* old_mm = curr->mm;
curr->mm = new_mm;
pmap::activate(new_mm->root_pa);
if (old_mm) old_mm->put();
/* 5a'. Transition from kernel thread to user process.
* Linux: begin_new_exec() in fs/exec.c:
* current->flags &= ~PF_KTHREAD; (line 1173)
* tsk->exit_signal = SIGCHLD; (line 1021)
* Without this, a kernel_execve'd task stays marked as a kthread
* and would be auto-reaped (exit_signal=-1) instead of becoming
* a proper zombie for parent wait(). */
curr->flags &= ~TF_KTHREAD;
curr->exit_signal = SIGCHLD;
/* 5b. Reset signal handlers to SIG_DFL (POSIX requirement).
*
* Linux fs/exec.c:flush_signal_handlers() runs under sighand->siglock.
* Even though after fork our sighand is no longer shared with the
* parent (we copy in task_clone), Linux still locks here — and so
* must we, because the same sighand may be shared with CLONE_SIGHAND
* sibling threads of the execing task. Holding siglock here also
* keeps the per-task blocked mask reset atomic w.r.t. concurrent
* sigprocmask / signal delivery on this very task. */
if (curr->sighand) {
LockGuard guard(curr->sighand->siglock);
for (int i = 0; i < _NSIG; i++) {
if (curr->sighand->action[i].sa_handler != SIG_IGN)
curr->sighand->action[i].sa_handler = SIG_DFL;
curr->sighand->action[i].sa_flags = 0;
curr->sighand->action[i].sa_mask.clear();
}
curr->blocked.clear();
} else {
curr->blocked.clear();
}
/* 5c. Close O_CLOEXEC file descriptors */
/* TODO: implement close-on-exec */
/* 6. Set up trapframe to enter user space */
arch::TrapFrame* tf = curr->trapframe;
if (!tf) {
printk(KERN_ERR "exec: no trapframe!\n");
task_kill_current(SIGKILL);
return -EINVAL; /* not reached */
}
/* Initialize trapframe for user-mode return.
* This is the MI equivalent of Linux's start_thread() + kernel
* metadata setup. All arch-specific details (sstatus, FP state,
* kernel_satp, kernel_sp, trap handler address, etc.) are handled
* by the MD layer. MI exec never touches arch registers. */
arch::trapframe_init_user(tf, info.entry, user_sp);
pr_debug_s(KLOG_SUBSYS_EXEC,
"exec: pid %d entry=%llx sp=%llx phdr=%llx\n",
curr->pid, info.entry, user_sp, info.phdr_addr);
/* Verify loaded data at entry point — debug-level, ring buffer only. */
{
uint64 pa;
if (pmap::lookup(curr->mm->root_pa, page_align_down(info.entry), &pa)) {
uint8* kva = reinterpret_cast<uint8*>(pa_to_va(PhysAddr{pa}).raw);
uint64 off = info.entry - page_align_down(info.entry);
pr_debug_s(KLOG_SUBSYS_EXEC,
"exec: entry bytes @%llx: %02x %02x %02x %02x %02x %02x %02x %02x\n",
info.entry,
kva[off+0], kva[off+1], kva[off+2], kva[off+3],
kva[off+4], kva[off+5], kva[off+6], kva[off+7]);
}
for (uint64 va = 0x10000; va < 0x14000; va += kPageSize) {
if (pmap::lookup(curr->mm->root_pa, va, &pa)) {
uint8* kva2 = reinterpret_cast<uint8*>(pa_to_va(PhysAddr{pa}).raw);
pr_debug_s(KLOG_SUBSYS_EXEC,
"exec: page %llx: %02x%02x%02x%02x %02x%02x%02x%02x\n",
va, kva2[0], kva2[1], kva2[2], kva2[3],
kva2[4], kva2[5], kva2[6], kva2[7]);
} else {
pr_debug_s(KLOG_SUBSYS_EXEC,
"exec: page %llx: NOT MAPPED\n", va);
}
}
}
/* Flush instruction cache after loading executable code into memory.
* Without this, the I-cache may contain stale data for the pages
* we just loaded, causing the CPU to execute garbage. */
arch::mmu::flush_icache();
return 0; /* Will return to user mode via trap return */
}
/* ================================================================== */
/* sys_execve_wrapper — syscall entry point */
/* ================================================================== */
int64 sys_execve_wrapper(uint64 a0, uint64 a1, uint64 a2,
uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/)
{
const char* pathname = reinterpret_cast<const char*>(a0);
const char* const* argv = reinterpret_cast<const char* const*>(a1);
const char* const* envp = reinterpret_cast<const char* const*>(a2);
return do_execve(pathname, argv, envp);
}
/* ================================================================== */
/* kernel_execve — call execve from kernel context (e.g. init) */
/* ================================================================== */
int kernel_execve(const char* pathname, const char* const* argv,
const char* const* envp)
{
TaskControlBlock* curr = current_task();
int ret = do_execve(pathname, argv, envp);
if (ret != 0)
return ret;
/*
* do_execve succeeded: the trapframe is set up with entry point,
* user stack, and sstatus (SPP=0 for U-mode). We must NOT return
* normally — that would go back through the kthread return path
* and jump to address 0. Instead, jump directly to user mode via
* the same userret() path that fork_child_entry uses.
*
* Switch stvec from kernelvec to trap_vector so that traps from
* U-mode are handled by the user trap entry path (which loads
* kernel context from the trapframe via sscratch).
*/
arch::trap::set_vector(reinterpret_cast<uint64>(trap_vector));
userret(curr->trapframe);
__builtin_unreachable();
}
|