A Total Overhaul on the Lunaix's Virtual Memory Model (#26)
[lunaix-os.git] / lunaix-os / kernel / process / thread.c
1 #include <lunaix/process.h>
2 #include <lunaix/sched.h>
3 #include <lunaix/syscall.h>
4 #include <lunaix/syscall_utils.h>
5 #include <lunaix/mm/mmap.h>
6 #include <lunaix/mm/vmm.h>
7 #include <lunaix/mm/pmm.h>
8 #include <lunaix/syslog.h>
9
10 #include <usr/lunaix/threads.h>
11
12 #include <sys/abi.h>
13 #include <sys/mm/mm_defs.h>
14
15 LOG_MODULE("THREAD")
16
17 static ptr_t
18 __alloc_user_thread_stack(struct proc_info* proc, struct mm_region** stack_region, ptr_t vm_mnt)
19 {
20     ptr_t th_stack_top = (proc->thread_count + 1) * USR_STACK_SIZE;
21     th_stack_top = ROUNDUP(USR_STACK_END - th_stack_top, MEM_PAGE);
22
23     struct mm_region* vmr;
24     struct proc_mm* mm = vmspace(proc);
25     struct mmap_param param = { .vms_mnt = vm_mnt,
26                                 .pvms = mm,
27                                 .mlen = USR_STACK_SIZE,
28                                 .proct = PROT_READ | PROT_WRITE,
29                                 .flags = MAP_ANON | MAP_PRIVATE,
30                                 .type = REGION_TYPE_STACK };
31
32     int errno = mmap_user((void**)&th_stack_top, &vmr, th_stack_top, NULL, &param);
33
34     if (errno) {
35         WARN("failed to create user thread stack: %d", errno);
36         return 0;
37     }
38
39     set_pte(mkptep_va(vm_mnt, vmr->start), guard_pte);
40
41     *stack_region = vmr;
42
43     ptr_t stack_top = align_stack(th_stack_top + USR_STACK_SIZE - 1);
44     return stack_top;
45 }
46
47 static ptr_t
48 __alloc_kernel_thread_stack(struct proc_info* proc, ptr_t vm_mnt)
49 {
50     pfn_t kstack_top = leaf_count(KSTACK_AREA_END);
51     pfn_t kstack_end = pfn(KSTACK_AREA);
52     pte_t* ptep      = mkptep_pn(vm_mnt, kstack_top);
53     while (ptep_pfn(ptep) > kstack_end) {
54         ptep -= KSTACK_PAGES;
55
56         // first page in the kernel stack is guardian page
57         pte_t pte = *(ptep + 1);
58         if (pte_isnull(pte)) {
59             goto found;
60         }
61     }
62
63     WARN("failed to create kernel stack: max stack num reach\n");
64     return 0;
65
66 found:;
67     ptr_t pa = pmm_alloc_cpage(KSTACK_PAGES - 1, 0);
68
69     if (!pa) {
70         WARN("failed to create kernel stack: nomem\n");
71         return 0;
72     }
73
74     set_pte(ptep, guard_pte);
75
76     pte_t pte = mkpte(pa, KERNEL_DATA);
77     vmm_set_ptes_contig(ptep + 1, pte, LFT_SIZE, KSTACK_PAGES - 1);
78
79     ptep += KSTACK_PAGES;
80     return align_stack(ptep_va(ptep, LFT_SIZE) - 1);
81 }
82
83 void
84 thread_release_mem(struct thread* thread)
85 {
86     struct proc_mm* mm = vmspace(thread->process);
87     ptr_t vm_mnt = mm->vm_mnt;
88
89     // Ensure we have mounted
90     assert(vm_mnt);
91
92     pte_t* ptep = mkptep_va(vm_mnt, thread->kstack);
93     
94     ptep -= KSTACK_PAGES - 1;
95     vmm_unset_ptes(ptep, KSTACK_PAGES);
96     
97     if (thread->ustack) {
98         if ((thread->ustack->start & 0xfff)) {
99             fail("invalid ustack struct");
100         }
101         mem_unmap_region(vm_mnt, thread->ustack);
102     }
103 }
104
105 struct thread*
106 create_thread(struct proc_info* proc, bool with_ustack)
107 {
108     struct proc_mm* mm = vmspace(proc);
109     assert(mm->vm_mnt);
110
111     ptr_t vm_mnt = mm->vm_mnt;
112     struct mm_region* ustack_region = NULL;
113     if (with_ustack && 
114         !(__alloc_user_thread_stack(proc, &ustack_region, vm_mnt))) 
115     {
116         return NULL;
117     }
118
119     ptr_t kstack = __alloc_kernel_thread_stack(proc, vm_mnt);
120     if (!kstack) {
121         mem_unmap_region(vm_mnt, ustack_region);
122         return NULL;
123     }
124
125     struct thread* th = alloc_thread(proc);
126     if (!th) {
127         return NULL;
128     }
129     
130     th->kstack = kstack;
131     th->ustack = ustack_region;
132
133     return th;
134 }
135
136 void
137 start_thread(struct thread* th, ptr_t entry)
138 {
139     assert(th && entry);
140     struct proc_mm* mm = vmspace(th->process);
141
142     assert(mm->vm_mnt);
143     
144     struct transfer_context transfer;
145     if (!kernel_addr(entry)) {
146         assert(th->ustack);
147
148         ptr_t ustack_top = align_stack(th->ustack->end - 1);
149         ustack_top -= 16;   // pre_allocate a 16 byte for inject parameter
150         thread_create_user_transfer(&transfer, th->kstack, ustack_top, entry);
151
152         th->ustack_top = ustack_top;
153     } 
154     else {
155         thread_create_kernel_transfer(&transfer, th->kstack, entry);
156     }
157
158     inject_transfer_context(mm->vm_mnt, &transfer);
159     th->intr_ctx = (isr_param*)transfer.inject;
160
161     commit_thread(th);
162 }
163
164 void 
165 exit_thread(void* val) {
166     terminate_current_thread((ptr_t)val);
167     schedule();
168 }
169
170 struct thread*
171 thread_find(struct proc_info* proc, tid_t tid)
172 {
173     struct thread *pos, *n;
174     llist_for_each(pos, n, &proc->threads, proc_sibs) {
175         if (pos->tid == tid) {
176             return pos;
177         }
178     }
179
180     return NULL;
181 }
182
183 __DEFINE_LXSYSCALL4(int, th_create, tid_t*, tid, struct uthread_info*, thinfo, 
184                                     void*, entry, void*, param)
185 {
186     struct thread* th = create_thread(__current, true);
187     if (!th) {
188         return EAGAIN;
189     }
190
191     start_thread(th, (ptr_t)entry);
192
193     ptr_t ustack_top = th->ustack_top;
194     *((void**)ustack_top) = param;
195
196     thinfo->th_stack_sz = region_size(th->ustack);
197     thinfo->th_stack_top = (void*)ustack_top;
198     
199     if (tid) {
200         *tid = th->tid;
201     }
202
203     return 0;
204 }
205
206 __DEFINE_LXSYSCALL(tid_t, th_self)
207 {
208     return current_thread->tid;
209 }
210
211 __DEFINE_LXSYSCALL1(void, th_exit, void*, val)
212 {
213     exit_thread(val);
214 }
215
216 __DEFINE_LXSYSCALL2(int, th_join, tid_t, tid, void**, val_ptr)
217 {
218     struct thread* th = thread_find(__current, tid);
219     if (!th) {
220         return EINVAL;
221     }
222
223     if (th == current_thread) {
224         return EDEADLK;
225     }
226
227     while (!proc_terminated(th)) {
228         sched_pass();
229     }
230
231     if (val_ptr) {
232         *val_ptr = (void*)th->exit_val;
233     }
234
235     destory_thread(th);
236
237     return 0;
238 }
239
240 __DEFINE_LXSYSCALL1(int, th_detach, tid_t, tid)
241 {
242     // can not detach the only thread
243     if (__current->thread_count == 1) {
244         return EINVAL;
245     }
246
247     struct thread* th = thread_find(__current, tid);
248     if (!th) {
249         return EINVAL;
250     }
251
252     detach_thread(th);
253     return 0;
254 }
255
256 __DEFINE_LXSYSCALL2(int, th_kill, tid_t, tid, int, signum)
257 {
258     struct thread* target = thread_find(__current, tid);
259     if (!target) {
260         return EINVAL;
261     }
262
263     if (signum > _SIG_NUM) {
264         return EINVAL;
265     }
266
267     if (signum) {
268         thread_setsignal(target, signum);
269     }
270     
271     return 0;
272 }