Minor fixes
[meinos.git] / kernel2 / procm.c
blob6467b378c60d6ab8f4bf6eaf7b4cff7c56274a35
1 /*
2 meinOS - A unix-like x86 microkernel operating system
3 Copyright (C) 2008 Janosch Gräf <janosch.graef@gmx.net>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include <sys/types.h>
20 #include <stdint.h>
21 #include <memkernel.h>
22 #include <procm.h>
23 #include <llist.h>
24 #include <string.h>
25 #include <memuser.h>
26 #include <memphys.h>
27 #include <malloc.h>
28 #include <kprint.h>
29 #include <gdt.h>
30 #include <cpu.h>
31 #include <syscall.h>
32 #include <interrupt.h>
34 /**
35 * Initializes process management
36 * @return -1=Success; 0=Failure
38 int proc_init() {
39 proc_all = llist_create();
40 proc_running = llist_create();
41 proc_sleeping = llist_create();
42 proc_nextpid = 1;
43 proc_current = NULL;
44 if (syscall_create(SYSCALL_PROC_GETPID,proc_getpid,0)==-1) return -1;
45 if (syscall_create(SYSCALL_PROC_GETUID,proc_getuid,1)==-1) return -1;
46 if (syscall_create(SYSCALL_PROC_GETGID,proc_getgid,1)==-1) return -1;
47 if (syscall_create(SYSCALL_PROC_SETUID,proc_setuid,2)==-1) return -1;
48 if (syscall_create(SYSCALL_PROC_SETGID,proc_setgid,2)==-1) return -1;
49 if (syscall_create(SYSCALL_PROC_GETPARENT,proc_getparent,1)==-1) return -1;
50 if (syscall_create(SYSCALL_PROC_GETNAME,proc_getname,3)==-1) return -1;
51 if (syscall_create(SYSCALL_PROC_GETPIDBYNAME,proc_getpidbyname,1)==-1) return -1;
52 if (syscall_create(SYSCALL_PROC_GETVAR,proc_getvar,0)==-1) return -1;
53 if (syscall_create(SYSCALL_PROC_EXIT,proc_exit,1)==-1) return -1;
54 if (syscall_create(SYSCALL_PROC_ABORT,proc_abort,0)==-1) return -1;
55 if (syscall_create(SYSCALL_PROC_STOP,proc_stop,0)==-1) return -1;
56 return 0;
59 /**
60 * Creates a new process
61 * @param name Process name
62 * @return Process
64 proc_t *proc_create(char *name,uid_t uid,gid_t gid,proc_t *parent,int running) {
65 proc_t *new = malloc(sizeof(proc_t));
67 new->pid = proc_nextpid++;
68 new->uid = uid;
69 new->euid = uid;
70 new->suid = uid;
71 new->gid = gid;
72 new->egid = gid;
73 new->sgid = gid;
74 new->name = strdup(name);
75 new->parent = parent;
76 if (parent!=NULL) llist_push(parent->children,new);
77 new->children = llist_create();
78 memset(&(new->registers),0,sizeof(new->registers));
79 new->registers.efl = 0x202;
80 new->registers.cs = IDX2SEL(3,PRIV_USER);
81 new->registers.ds = IDX2SEL(4,PRIV_USER);
82 new->registers.es = IDX2SEL(4,PRIV_USER);
83 new->registers.fs = IDX2SEL(4,PRIV_USER);
84 new->registers.gs = IDX2SEL(4,PRIV_USER);
85 new->registers.ss = IDX2SEL(4,PRIV_USER);
86 new->addrspace = memuser_create_addrspace(new);
87 new->registers.esp = (uint32_t)memuser_create_stack(new->addrspace);
88 //memset(&(new->irq_handler),0,sizeof(new->irq_handler));
89 new->time_handler = llist_create();
90 new->nice = 0;
91 new->ticks_rem = NICE2TICKS(new->nice);
92 //new->ipc_objects = llist_create();
93 new->var = -1;
94 new->defunc = 0;
95 new->is_sleeping = !running;
96 new->signal = NULL;
97 new->is_vm86 = 0;
99 llist_push(proc_all,new);
100 llist_push(running?proc_running:proc_sleeping,new);
101 return new;
105 * Destroys a process
106 * @param proc Process
107 * @return Success?
109 int proc_destroy(proc_t *proc) {
110 proc_t *child;
112 free(proc->name);
113 while ((child = llist_pop(proc->children))) child->parent = proc->parent;
114 llist_destroy(proc->children);
115 if (proc->parent!=NULL) llist_remove(proc->parent->children,llist_find(proc->parent->children,proc));
116 if (proc->addrspace!=NULL) memuser_destroy_addrspace(proc->addrspace);
117 llist_destroy(proc->time_handler);
119 llist_remove(proc_all,llist_find(proc_all,proc));
120 if (llist_remove(proc_running,llist_find(proc_running,proc))!=proc) llist_remove(proc_sleeping,llist_find(proc_sleeping,proc));
121 free(proc);
122 return 0;
126 * Finds a process by PID
127 * @param PID
128 * @return Process
130 proc_t *proc_find(pid_t pid) {
131 size_t i;
132 proc_t *proc;
133 if (proc_current->pid==pid) return proc_current;
134 for (i=0;(proc = llist_get(proc_all,i));i++) {
135 if (proc->pid==pid) return proc;
137 return NULL;
141 * Sends a process sleeping
142 * @param proc Process
143 * @param sleep Reference to sleep variable
144 * @return Success?
146 int proc_sleep(proc_t *proc) {
147 if (!proc->is_sleeping) {
148 llist_remove(proc_running,llist_find(proc_running,proc));
149 llist_push(proc_sleeping,proc);
150 proc->is_sleeping = 1;
151 if (proc==proc_current) proc_shedule();
153 return 0;
157 * Wakes a process
158 * @param proc Process
159 * @return Success?
161 int proc_wake(proc_t *proc) {
162 if (proc->is_sleeping) {
163 llist_remove(proc_sleeping,llist_find(proc_sleeping,proc));
164 llist_push(proc_running,proc);
165 proc->is_sleeping = 0;
167 return 0;
171 * Save registers of process
172 * @param proc Process
173 * @return Success?
175 int proc_regs_save(proc_t *proc) {
176 proc->registers.eax = *interrupt_curregs.eax;
177 proc->registers.ebx = *interrupt_curregs.ebx;
178 proc->registers.ecx = *interrupt_curregs.ecx;
179 proc->registers.edx = *interrupt_curregs.edx;
180 proc->registers.esi = *interrupt_curregs.esi;
181 proc->registers.edi = *interrupt_curregs.edi;
182 proc->registers.ebp = *interrupt_curregs.ebp;
183 proc->registers.esp = *interrupt_curregs.esp;
184 proc->registers.eip = *interrupt_curregs.eip;
185 proc->registers.efl = *interrupt_curregs.efl;
186 proc->registers.cs = *interrupt_curregs.cs;
187 proc->registers.ds = *interrupt_curregs.ds;
188 proc->registers.es = *interrupt_curregs.es;
189 proc->registers.fs = *interrupt_curregs.fs;
190 proc->registers.gs = *interrupt_curregs.gs;
191 proc->registers.ss = *interrupt_curregs.ss;
192 if (proc->is_vm86) vm86_save_segregs(proc);
193 return 0;
197 * Load registers of process
198 * @param proc Process
199 * @return Success?
201 int proc_regs_load(proc_t *proc) {
202 *interrupt_curregs.eax = proc->registers.eax;
203 *interrupt_curregs.ebx = proc->registers.ebx;
204 *interrupt_curregs.ecx = proc->registers.ecx;
205 *interrupt_curregs.edx = proc->registers.edx;
206 *interrupt_curregs.esi = proc->registers.esi;
207 *interrupt_curregs.edi = proc->registers.edi;
208 *interrupt_curregs.ebp = proc->registers.ebp;
209 *interrupt_curregs.esp = proc->registers.esp;
210 *interrupt_curregs.eip = proc->registers.eip;
211 *interrupt_curregs.efl = proc->registers.efl;
212 *interrupt_curregs.cs = proc->registers.cs;
213 *interrupt_curregs.ds = proc->registers.ds;
214 *interrupt_curregs.es = proc->registers.es;
215 *interrupt_curregs.fs = proc->registers.fs;
216 *interrupt_curregs.gs = proc->registers.gs;
217 *interrupt_curregs.ss = proc->registers.ss;
218 if (proc->is_vm86) vm86_load_segregs(proc);
219 return 0;
223 * Loads next process for execution
225 void proc_shedule() {
226 size_t i;
227 proc_t *proc;
228 proc_t *proc_old = proc_current;
230 // if no processes running hold machine (until next interrupt)
231 if (llist_empty(proc_running)) {
232 // if no processes at all, shutdown
233 if (llist_empty(proc_sleeping)) cpu_shutdown();
234 else {
235 /// @todo maybe put these 2 lines in proc_idle
236 proc_regs_save(proc_current);
237 proc_current = NULL;
238 proc_idle();
242 // Process finished its time slice
243 if (proc_current!=NULL) {
244 proc_current->ticks_rem--;
245 proc_current = NULL;
248 // Search for process that still has time
249 for (i=0;(proc = llist_get(proc_running,i));i++) {
250 if (proc->ticks_rem>0) proc_current = proc;
253 // If no processes with time, fill time slices
254 if (proc_current==NULL) {
255 //kprintf("Refilling time slices\n");
256 for (i=0;(proc = llist_get(proc_running,i));i++) proc->ticks_rem = NICE2TICKS(proc->nice);
257 proc_current = llist_get(proc_running,0);
260 // Set context and load address space
261 if (proc_old!=proc_current) {
262 if (proc_old!=NULL) proc_regs_save(proc_old);
263 proc_regs_load(proc_current);
264 memuser_load_addrspace(proc_current->addrspace);
269 * Gets PID (Syscall)
270 * @return PID
272 pid_t proc_getpid() {
273 return proc_current->pid;
277 * Gets Parent PID (Syscall)
278 * @param pid Process to get parent's PID of
279 * @return Parent's PID
281 pid_t proc_getparent(pid_t pid) {
282 proc_t *proc = proc_find(pid);
283 if (proc!=NULL) {
284 if (proc->parent!=NULL) return proc->parent->pid;
286 return 0;
290 * Gets UID (Syscall)
291 * @param idmask Which ID to return
292 * @return UID
294 uid_t proc_getuid(int idmask) {
295 if ((idmask&1)) return proc_current->uid;
296 else if ((idmask&2)) return proc_current->euid;
297 else if ((idmask&4)) return proc_current->suid;
298 else return 0;
302 * Sets UID (Syscall)
303 * @param idmask Which ID to set
304 * @param uid New UID
305 * @todo Check permissions
307 void proc_setuid(int idmask,uid_t uid) {
308 if ((idmask&1)) proc_current->uid = uid;
309 else if ((idmask&2)) proc_current->euid = uid;
310 else if ((idmask&4)) proc_current->suid = uid;
314 * Gets GID (Syscall)
315 * @param idmask Which ID to return
316 * @return GID
318 gid_t proc_getgid(int idmask) {
319 if ((idmask&1)) return proc_current->gid;
320 else if ((idmask&2)) return proc_current->egid;
321 else if ((idmask&4)) return proc_current->sgid;
322 else return 0;
326 * Sets GID (Syscall)
327 * @param idmask Which ID to set
328 * @param uid New GID
329 * @todo Check permissions
331 void proc_setgid(int idmask,gid_t gid) {
332 if ((idmask&1)) proc_current->gid = gid;
333 else if ((idmask&2)) proc_current->egid = gid;
334 else if ((idmask&4)) proc_current->sgid = gid;
338 * Gets process name (Syscall)
339 * @param pid PID
340 * @param buf Buffer for name
341 * @param maxlen Maximal length of name
342 * @return Success? (if buf==NULL length of name is returned)
344 ssize_t proc_getname(pid_t pid,char *buf,size_t maxlen) {
345 proc_t *proc = proc_find(pid);
346 if (proc!=NULL) {
347 if (buf!=NULL) {
348 strncpy(buf,proc->name,maxlen);
349 return 0;
351 else return strlen(proc->name)+1;
353 else return -1;
357 * Gets PID by process name
358 * @param name Process name
359 * @return PID of process
361 pid_t proc_getpidbyname(const char *name) {
362 size_t i;
363 proc_t *proc;
364 for (i=0;(proc = llist_get(proc_all,i));i++) {
365 if (strcmp(proc->name,name)==0) return proc->pid;
367 return -1;
371 * Gets private variable
372 * @return private variable
374 int proc_getvar() {
375 return proc_current->var;
379 * Exits process (Syscall)
380 * @param ret Return value
382 void proc_exit(int ret) {
383 proc_current->ret = ret;
384 proc_current->defunc = 1;
385 llist_remove(proc_running,llist_find(proc_running,proc_current));
386 //memuser_destroy_addrspace(proc_current->addrspace);
387 //proc_current->addrspace = NULL;
388 proc_current = NULL;
389 proc_idle();
393 * Aborts process (Syscall)
395 void proc_abort() {
396 kprintf("Program aborted: %s #%d\n",proc_current->name,proc_current->pid);
397 proc_exit(1);
401 * Stops process (Syscall)
403 void proc_stop() {
404 proc_sleep(proc_current);
408 * Calls a function in process
409 * @param proc Process
410 * @param func Function
411 * @param numparams Number of parameters
412 * @param ... Parameters
414 void proc_call(proc_t *proc,void *func,size_t numparams,...) {
415 if (!proc->defunc) {
416 va_list args;
417 size_t i;
418 int *params = malloc(numparams*sizeof(int));
419 uint32_t *eip = proc_current==proc?interrupt_curregs.eip:&proc->registers.eip;
421 va_start(args,numparams);
422 for (i=0;i<numparams;i++) params[numparams-(i+1)] = va_arg(args,int);
423 va_end(args);
425 memuser_load_addrspace(proc->addrspace);
426 for (i=0;i<numparams;i++) proc_push(proc,params[i]);
427 proc_push(proc,*eip);
428 if (proc_current!=NULL) {
429 memuser_load_addrspace(proc_current->addrspace);
431 *eip = (uint32_t)func;
433 //proc_wake(proc);
438 * Push an element on user stack
439 * @param proc Process
440 * @param val Value
442 void proc_push(proc_t *proc,int val) {
443 uint32_t *esp = proc_current==proc?interrupt_curregs.esp:&proc->registers.esp;
444 *esp -= sizeof(int);
445 //memuser_load_addrspace(proc->addrspace);
446 *((int*)(*esp)) = val;
447 //memuser_load_addrspace(proc_current->addrspace);
451 * Pops an element from user stack
452 * @param proc Process
453 * @return Value
455 int proc_pop(proc_t *proc) {
456 int val = *((int*)proc->registers.esp);
457 proc->registers.esp += sizeof(int);
458 return val;
462 * Idles until next schedule
464 void proc_idle() {
465 asm("mov %0,%%esp"::"r"(cpu_this->tss->esp0)); // reload ESP0 in TSS "by hand"
466 interrupt_enable(1);
467 cpu_halt();
471 * Forks a process (share virtual memory)
472 * @param proc Process to be forked
473 * @return New process
475 proc_t *proc_vfork(proc_t *proc) {
476 proc_t *new = malloc(sizeof(proc_t));
477 memcpy(new,proc,sizeof(proc_t));
478 new->pid = proc_nextpid++;
479 new->parent = proc;
480 new->name = strdup(proc->name);
481 llist_push(proc->children,new);
482 new->time_handler = llist_copy(proc->time_handler);
483 new->ticks_rem = NICE2TICKS(new->nice);
484 llist_push(proc_all,new);
485 llist_push(proc_running,new);
486 return new;
490 * Forks a process
491 * @param proc Process to be forked
492 * @return New process
494 proc_t *proc_fork(proc_t *proc) {
495 proc_t *new = proc_vfork(proc);
496 new->addrspace = memuser_clone_addrspace(new,proc->addrspace);
497 return new;