1#include <asm.h>
2#include <console/printf.h>
3#include <elf.h>
4#include <mem/page.h>
5#include <mem/pmm.h>
6#include <mem/vmm.h>
7#include <stdbool.h>
8#include <stdint.h>
9#include <string.h>
10
11#define ELF_MAGIC 0x464C457F // "\x7FELF"
12
13#define USER_STACK_TOP 0x7FFFFFF000
14#define USER_STACK_SIZE (16 * PAGE_SIZE)
15
16uint64_t elf_load(const void *elf_data) {
17 struct elf64_ehdr *ehdr = (struct elf64_ehdr *) elf_data;
18
19 if (ehdr->ident.magic != ELF_MAGIC || ehdr->ident.class != 2) {
20 printf(format: "Invalid ELF64\n");
21 return 0;
22 }
23
24 struct elf64_phdr *phdrs =
25 (struct elf64_phdr *) ((uint8_t *) elf_data + ehdr->phoff);
26
27 for (int i = 0; i < ehdr->phnum; i++) {
28 struct elf64_phdr *ph = &phdrs[i];
29 if (ph->type != 1)
30 continue;
31
32 uint64_t va_start = PAGE_ALIGN_DOWN(ph->vaddr);
33 uint64_t va_end = PAGE_ALIGN_UP(ph->vaddr + ph->memsz);
34
35 for (uint64_t va = va_start; va < va_end; va += 0x1000) {
36 uint64_t phys = pmm_alloc_page();
37 vmm_map_page(va, phys,
38 PAGE_PRESENT | PAGE_USER_ALLOWED | PAGE_WRITE,
39 VMM_FLAG_NONE);
40 }
41 }
42
43 return ehdr->entry;
44}
45
46void elf_map(uintptr_t user_pml4_phys, void *elf_data) {
47 struct elf64_ehdr *ehdr = (struct elf64_ehdr *) elf_data;
48 struct elf64_phdr *phdrs =
49 (struct elf64_phdr *) ((uint8_t *) elf_data + ehdr->phoff);
50
51 for (int i = 0; i < ehdr->phnum; i++) {
52 struct elf64_phdr *ph = &phdrs[i];
53 if (ph->type != PT_LOAD)
54 continue;
55
56 uintptr_t seg_vaddr_start = PAGE_ALIGN_DOWN(ph->vaddr);
57 uintptr_t seg_vaddr_end = PAGE_ALIGN_UP(ph->vaddr + ph->memsz);
58 uintptr_t file_start = ph->offset;
59 uintptr_t file_end = ph->offset + ph->filesz;
60
61 uint64_t flags = PAGE_USER_ALLOWED | PAGE_PRESENT;
62 if (ph->flags & PF_W)
63 flags |= PAGE_WRITE;
64 if (!(ph->flags & PF_X))
65 flags |= PAGE_XD;
66
67 for (vaddr_t vaddr = seg_vaddr_start; vaddr < seg_vaddr_end;
68 vaddr += PAGE_SIZE) {
69
70 uintptr_t phys = pmm_alloc_page();
71 if (!phys)
72 panic("Failed to allocate page for user ELF segment");
73
74 void *phys_mapped = vmm_map_bump(phys, PAGE_SIZE, 0);
75 memset(phys_mapped, 0, PAGE_SIZE);
76
77 uintptr_t offset_in_seg = vaddr - seg_vaddr_start;
78 uintptr_t file_offset_in_seg =
79 offset_in_seg + (ph->vaddr - seg_vaddr_start);
80 uintptr_t file_pos = file_start + file_offset_in_seg;
81
82 uintptr_t page_offset = 0;
83 if (vaddr == seg_vaddr_start)
84 page_offset = ph->vaddr & (PAGE_SIZE - 1);
85
86 uint64_t to_copy = 0;
87 if (file_pos < file_end) {
88 uint64_t bytes_remaining = file_end - file_pos;
89 to_copy = PAGE_SIZE - page_offset;
90 if (bytes_remaining < to_copy)
91 to_copy = bytes_remaining;
92
93 memcpy((uint8_t *) phys_mapped + page_offset,
94 (uint8_t *) elf_data + file_pos, to_copy);
95 }
96
97 vmm_map_page_user(vmm_phys_to_pml4(user_pml4_phys), vaddr, phys,
98 flags, VMM_FLAG_USER);
99 }
100 }
101}
102
103uintptr_t map_user_stack(uintptr_t user_pml4_phys) {
104 uintptr_t stack_bottom = USER_STACK_TOP - USER_STACK_SIZE;
105
106 for (uintptr_t v = stack_bottom; v < USER_STACK_TOP; v += PAGE_SIZE) {
107 uintptr_t phys = pmm_alloc_page();
108 if (!phys)
109 panic("Failed to alloc user stack");
110
111 vmm_map_page_user(vmm_phys_to_pml4(user_pml4_phys), v, phys,
112 PAGE_WRITE | PAGE_USER_ALLOWED | PAGE_PRESENT,
113 VMM_FLAG_USER);
114 }
115
116 return USER_STACK_TOP - 0x2000;
117}
118
119void syscall_setup(void *syscall_entry) {
120 uint64_t efer = rdmsr(msr: 0xC0000080);
121 efer |= (1 << 0); // SCE: Enable syscall/sysret
122 wrmsr(msr: 0xC0000080, value: efer);
123
124 wrmsr(msr: 0xC0000082, value: (uint64_t) syscall_entry);
125
126 wrmsr(msr: 0xC0000084, value: (1 << 9));
127
128 uint64_t star = ((uint64_t) 0x08 << 32) | ((uint64_t) 0x28 << 48);
129 wrmsr(msr: 0xC0000081, value: star);
130}
131
132__attribute__((noreturn)) void
133enter_userspace(uintptr_t entry_point, uintptr_t user_stack_top,
134 uint16_t user_cs, uint16_t user_ss, uintptr_t user_pml4_phys) {
135 asm volatile("mov %0, %%cr3" : : "r"(user_pml4_phys) : "memory");
136
137 uint64_t rflags;
138 asm volatile("pushfq; popq %0" : "=r"(rflags));
139 rflags |= (1 << 9);
140
141 asm volatile("cli\n"
142 "pushq %0\n" // SS
143 "pushq %1\n" // RSP
144 "pushq %2\n" // RFLAGS
145 "pushq %3\n" // CS
146 "pushq %4\n" // RIP
147 "iretq\n"
148 :
149 : "r"((uint64_t) user_ss), "r"(user_stack_top), "r"(rflags),
150 "r"((uint64_t) user_cs), "r"(entry_point)
151 : "memory");
152
153 __builtin_unreachable();
154}
155