kern

view src/proc.c @ 63:f44bec97a0ec

added errno.h in klibc
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 17 Aug 2011 05:42:44 +0300
parents ef9cc2e90277
children 0a205396e1a0
line source
1 #include <stdio.h>
2 #include <string.h>
3 #include <assert.h>
4 #include <errno.h>
5 #include "config.h"
6 #include "proc.h"
7 #include "tss.h"
8 #include "vm.h"
9 #include "segm.h"
10 #include "intr.h"
11 #include "panic.h"
12 #include "syscall.h"
13 #include "sched.h"
14 #include "tss.h"
16 #define FLAGS_INTR_BIT (1 << 9)
18 static void start_first_proc(void);
20 /* defined in proc-asm.S */
21 uint32_t switch_stack(uint32_t new_stack, uint32_t *old_stack);
22 void just_forked(void);
24 /* defined in test_proc.S */
25 void test_proc(void);
26 void test_proc_end(void);
28 static struct process proc[MAX_PROC];
30 /* cur_pid: pid of the currently executing process.
31 * when we're in the idle process cur_pid will be 0.
32 * last_pid: pid of the last real process that was running, this should
33 * never become 0. Essentially this defines the active kernel stack.
34 */
35 static int cur_pid, last_pid;
37 static struct task_state *tss;
40 void init_proc(void)
41 {
42 int tss_page;
44 /* allocate a page for the task state segment, to make sure
45 * it doesn't cross page boundaries
46 */
47 if((tss_page = pgalloc(1, MEM_KERNEL)) == -1) {
48 panic("failed to allocate memory for the task state segment\n");
49 }
50 tss = (struct task_state*)PAGE_TO_ADDR(tss_page);
52 /* the kernel stack segment never changes so we might as well set it now
53 * the only other thing that we use in the tss is the kernel stack pointer
54 * which is different for each process, and thus managed by context_switch
55 */
56 memset(tss, 0, sizeof *tss);
57 tss->ss0 = selector(SEGM_KDATA, 0);
59 set_tss((uint32_t)tss);
61 /* initialize system call handler (see syscall.c) */
62 init_syscall();
64 start_first_proc(); /* XXX never returns */
65 }
67 static void start_first_proc(void)
68 {
69 struct process *p;
70 int proc_size_pg, img_start_pg, stack_pg;
71 uint32_t img_start_addr;
72 struct intr_frame ifrm;
74 /* prepare the first process */
75 p = proc + 1;
76 p->id = 1;
77 p->parent = 0; /* no parent for init */
79 p->ticks_left = TIMESLICE_TICKS;
80 p->next = p->prev = 0;
82 /* the first process may keep this existing page table */
83 p->ctx.pgtbl_paddr = get_pgdir_addr();
85 /* allocate a chunk of memory for the process image
86 * and copy the code of test_proc there.
87 */
88 proc_size_pg = (test_proc_end - test_proc) / PGSIZE + 1;
89 if((img_start_pg = pgalloc(proc_size_pg, MEM_USER)) == -1) {
90 panic("failed to allocate space for the init process image\n");
91 }
92 img_start_addr = PAGE_TO_ADDR(img_start_pg);
93 memcpy((void*)img_start_addr, test_proc, proc_size_pg * PGSIZE);
94 printf("copied init process at: %x\n", img_start_addr);
96 /* allocate the first page of the process stack */
97 stack_pg = ADDR_TO_PAGE(KMEM_START) - 1;
98 if(pgalloc_vrange(stack_pg, 1) == -1) {
99 panic("failed to allocate user stack page\n");
100 }
101 p->user_stack_pg = stack_pg;
103 /* allocate a kernel stack for this process */
104 if((p->kern_stack_pg = pgalloc(KERN_STACK_SIZE / PGSIZE, MEM_KERNEL)) == -1) {
105 panic("failed to allocate kernel stack for the init process\n");
106 }
107 /* when switching from user space to kernel space, the ss0:esp0 from TSS
108 * will be used to switch to the per-process kernel stack, so we need to
109 * set it correctly before switching to user space.
110 * tss->ss0 is already set in init_proc above.
111 */
112 tss->esp0 = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE;
115 /* now we need to fill in the fake interrupt stack frame */
116 memset(&ifrm, 0, sizeof ifrm);
117 /* after the priviledge switch, this ss:esp will be used in userspace */
118 ifrm.esp = PAGE_TO_ADDR(stack_pg) + PGSIZE;
119 ifrm.ss = selector(SEGM_UDATA, 3);
120 /* instruction pointer at the beginning of the process image */
121 ifrm.eip = img_start_addr;
122 ifrm.cs = selector(SEGM_UCODE, 3);
123 /* make sure the user will run with interrupts enabled */
124 ifrm.eflags = FLAGS_INTR_BIT;
125 /* user data selectors should all be the same */
126 ifrm.ds = ifrm.es = ifrm.fs = ifrm.gs = ifrm.ss;
128 /* add it to the scheduler queues */
129 add_proc(p->id);
131 /* make it current */
132 set_current_pid(p->id);
134 /* execute a fake return from interrupt with the fake stack frame */
135 intr_ret(ifrm);
136 }
138 int fork(void)
139 {
140 int i, pid;
141 struct process *p, *parent;
143 disable_intr();
145 /* find a free process slot */
146 /* TODO don't search up to MAX_PROC if uid != 0 */
147 pid = -1;
148 for(i=1; i<MAX_PROC; i++) {
149 if(proc[i].id == 0) {
150 pid = i;
151 break;
152 }
153 }
155 if(pid == -1) {
156 /* process table full */
157 return -EAGAIN;
158 }
161 p = proc + pid;
162 parent = get_current_proc();
164 /* allocate a kernel stack for the new process */
165 if((p->kern_stack_pg = pgalloc(KERN_STACK_SIZE / PGSIZE, MEM_KERNEL)) == -1) {
166 return -EAGAIN;
167 }
168 p->ctx.stack_ptr = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE;
169 /* we need to copy the current interrupt frame to the new kernel stack so
170 * that the new process will return to the same point as the parent, just
171 * after the fork syscall.
172 */
173 p->ctx.stack_ptr -= sizeof(struct intr_frame);
174 memcpy((void*)p->ctx.stack_ptr, get_intr_frame(), sizeof(struct intr_frame));
175 /* child's return from fork returns 0 */
176 ((struct intr_frame*)p->ctx.stack_ptr)->regs.eax = 0;
178 /* we also need the address of just_forked in the stack, so that switch_stacks
179 * called from context_switch, will return to just_forked when we first switch
180 * to a newly forked process. just_forked then just calls intr_ret to return to
181 * userspace with the already constructed interrupt frame (see above).
182 */
183 p->ctx.stack_ptr -= 4;
184 *(uint32_t*)p->ctx.stack_ptr = (uint32_t)just_forked;
186 /* initialize the rest of the process structure */
187 p->id = pid;
188 p->parent = parent->id;
189 p->next = p->prev = 0;
191 /* will be copied on write */
192 p->user_stack_pg = parent->user_stack_pg;
194 p->ctx.pgtbl_paddr = clone_vm(CLONE_COW);
196 /* done, now let's add it to the scheduler runqueue */
197 add_proc(p->id);
199 return pid;
200 }
202 void context_switch(int pid)
203 {
204 static struct process *prev, *new;
206 assert(get_intr_state() == 0);
207 assert(pid > 0);
208 assert(last_pid > 0);
210 prev = proc + last_pid;
211 new = proc + pid;
213 if(last_pid != pid) {
214 set_current_pid(new->id);
216 /* switch to the new process' address space */
217 set_pgdir_addr(new->ctx.pgtbl_paddr);
219 /* make sure we'll return to the correct kernel stack next time
220 * we enter from userspace
221 */
222 tss->esp0 = PAGE_TO_ADDR(new->kern_stack_pg) + KERN_STACK_SIZE;
224 /* push all registers onto the stack before switching stacks */
225 push_regs();
227 /* XXX: when switching to newly forked processes this switch_stack call
228 * WILL NOT RETURN HERE. It will return to just_forked instead. So the
229 * rest of this function will not run.
230 */
231 switch_stack(new->ctx.stack_ptr, &prev->ctx.stack_ptr);
233 /* restore registers from the new stack */
234 pop_regs();
235 } else {
236 set_current_pid(new->id);
237 }
238 }
241 void set_current_pid(int pid)
242 {
243 cur_pid = pid;
244 if(pid > 0) {
245 last_pid = pid;
246 }
247 }
249 int get_current_pid(void)
250 {
251 return cur_pid;
252 }
254 struct process *get_current_proc(void)
255 {
256 return cur_pid > 0 ? &proc[cur_pid] : 0;
257 }
259 struct process *get_process(int pid)
260 {
261 return &proc[pid];
262 }