userspace fun: maze game and a new device to support it
[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_THREAD;
21     th_stack_top = ROUNDUP(USR_STACK_END - th_stack_top, PAGE_SIZE);
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_THREAD,
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     pte_t* guardp = mkptep_va(vm_mnt, vmr->start);
41     set_pte(guardp, guard_pte);
42
43     *stack_region = vmr;
44
45     ptr_t stack_top = align_stack(th_stack_top + USR_STACK_SIZE_THREAD - 1);
46     return stack_top;
47 }
48
49 static ptr_t
50 __alloc_kernel_thread_stack(struct proc_info* proc, ptr_t vm_mnt)
51 {
52     pfn_t kstack_top = leaf_count(KSTACK_AREA_END);
53     pfn_t kstack_end = pfn(KSTACK_AREA);
54     pte_t* ptep      = mkptep_pn(vm_mnt, kstack_top);
55     while (ptep_pfn(ptep) > kstack_end) {
56         ptep -= KSTACK_PAGES + 1;
57
58         pte_t pte = pte_at(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     unsigned int po = count_order(KSTACK_PAGES);
69     struct leaflet* leaflet = alloc_leaflet(po);
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     if (ustack_region) {
139         th->ustack_top = align_stack(ustack_region->end - 1);
140     }
141
142     return th;
143 }
144
145 void
146 start_thread(struct thread* th, ptr_t entry)
147 {
148     assert(th && entry);
149     struct proc_mm* mm = vmspace(th->process);
150
151     assert(mm->vm_mnt);
152     
153     struct hart_transition transition;
154     if (!kernel_addr(entry)) {
155         assert(th->ustack);
156
157         hart_user_transfer(&transition, th->kstack, th->ustack_top, entry);
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_LXSYSCALL3(int, th_create, tid_t*, tid, 
189                         struct uthread_param*, thparam, void*, entry)
190 {
191     struct thread* th = create_thread(__current, true);
192     if (!th) {
193         return EAGAIN;
194     }
195
196     ptr_t ustack_top;
197
198     ustack_top = th->ustack_top;
199     ustack_top = align_stack(ustack_top - sizeof(*thparam));
200
201     memcpy((void*)ustack_top, thparam, sizeof(*thparam));
202
203     th->ustack_top = ustack_top;
204     start_thread(th, (ptr_t)entry);
205     
206     if (tid) {
207         *tid = th->tid;
208     }
209
210     return 0;
211 }
212
213 __DEFINE_LXSYSCALL(tid_t, th_self)
214 {
215     return current_thread->tid;
216 }
217
218 __DEFINE_LXSYSCALL1(void, th_exit, void*, val)
219 {
220     exit_thread(val);
221 }
222
223 __DEFINE_LXSYSCALL2(int, th_join, tid_t, tid, void**, val_ptr)
224 {
225     struct thread* th = thread_find(__current, tid);
226     if (!th) {
227         return EINVAL;
228     }
229
230     if (th == current_thread) {
231         return EDEADLK;
232     }
233
234     while (!proc_terminated(th)) {
235         sched_pass();
236     }
237
238     if (val_ptr) {
239         *val_ptr = (void*)th->exit_val;
240     }
241
242     destory_thread(th);
243
244     return 0;
245 }
246
247 __DEFINE_LXSYSCALL1(int, th_detach, tid_t, tid)
248 {
249     // can not detach the only thread
250     if (__current->thread_count == 1) {
251         return EINVAL;
252     }
253
254     struct thread* th = thread_find(__current, tid);
255     if (!th) {
256         return EINVAL;
257     }
258
259     detach_thread(th);
260     return 0;
261 }
262
263 __DEFINE_LXSYSCALL2(int, th_kill, tid_t, tid, int, signum)
264 {
265     struct thread* target = thread_find(__current, tid);
266     if (!target) {
267         return EINVAL;
268     }
269
270     if (signum > _SIG_NUM) {
271         return EINVAL;
272     }
273
274     if (signum) {
275         thread_setsignal(target, signum);
276     }
277     
278     return 0;
279 }