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