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