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