rev |
line source |
nuclear@52
|
1 #include <stdio.h>
|
nuclear@47
|
2 #include <string.h>
|
nuclear@52
|
3 #include <assert.h>
|
nuclear@55
|
4 #include "config.h"
|
nuclear@42
|
5 #include "proc.h"
|
nuclear@42
|
6 #include "tss.h"
|
nuclear@45
|
7 #include "vm.h"
|
nuclear@47
|
8 #include "segm.h"
|
nuclear@47
|
9 #include "intr.h"
|
nuclear@47
|
10 #include "panic.h"
|
nuclear@51
|
11 #include "syscall.h"
|
nuclear@51
|
12 #include "sched.h"
|
nuclear@54
|
13 #include "tss.h"
|
nuclear@47
|
14
|
nuclear@55
|
15 #define FLAGS_INTR_BIT (1 << 9)
|
nuclear@47
|
16
|
nuclear@54
|
17 static void start_first_proc(void);
|
nuclear@54
|
18
|
nuclear@55
|
19 /* defined in proc-asm.S */
|
nuclear@55
|
20 uint32_t switch_stack(uint32_t new_stack);
|
nuclear@54
|
21
|
nuclear@47
|
22 /* defined in test_proc.S */
|
nuclear@47
|
23 void test_proc(void);
|
nuclear@47
|
24 void test_proc_end(void);
|
nuclear@42
|
25
|
nuclear@42
|
26 static struct process proc[MAX_PROC];
|
nuclear@42
|
27 static int cur_pid;
|
nuclear@42
|
28
|
nuclear@54
|
29 static struct task_state *tss;
|
nuclear@54
|
30
|
nuclear@54
|
31
|
nuclear@42
|
32 void init_proc(void)
|
nuclear@42
|
33 {
|
nuclear@54
|
34 int tss_page;
|
nuclear@51
|
35
|
nuclear@54
|
36 /* allocate a page for the task state segment, to make sure
|
nuclear@54
|
37 * it doesn't cross page boundaries
|
nuclear@54
|
38 */
|
nuclear@54
|
39 if((tss_page = pgalloc(1, MEM_KERNEL)) == -1) {
|
nuclear@54
|
40 panic("failed to allocate memory for the task state segment\n");
|
nuclear@54
|
41 }
|
nuclear@55
|
42 tss = (struct task_state*)PAGE_TO_ADDR(tss_page);
|
nuclear@54
|
43
|
nuclear@54
|
44 /* the kernel stack segment never changes so we might as well set it now
|
nuclear@54
|
45 * the only other thing that we use in the tss is the kernel stack pointer
|
nuclear@54
|
46 * which is different for each process, and thus managed by context_switch
|
nuclear@54
|
47 */
|
nuclear@54
|
48 memset(tss, 0, sizeof *tss);
|
nuclear@54
|
49 tss->ss0 = selector(SEGM_KDATA, 0);
|
nuclear@54
|
50
|
nuclear@55
|
51 set_tss((uint32_t)tss);
|
nuclear@54
|
52
|
nuclear@54
|
53 /* initialize system call handler (see syscall.c) */
|
nuclear@51
|
54 init_syscall();
|
nuclear@42
|
55
|
nuclear@54
|
56 start_first_proc(); /* XXX never returns */
|
nuclear@54
|
57 }
|
nuclear@54
|
58
|
nuclear@54
|
59
|
nuclear@54
|
60 static void start_first_proc(void)
|
nuclear@54
|
61 {
|
nuclear@54
|
62 struct process *p;
|
nuclear@54
|
63 int proc_size_pg, img_start_pg, stack_pg;
|
nuclear@55
|
64 uint32_t img_start_addr;
|
nuclear@54
|
65 struct intr_frame ifrm;
|
nuclear@54
|
66
|
nuclear@42
|
67 /* prepare the first process */
|
nuclear@54
|
68 p = proc + 1;
|
nuclear@54
|
69 p->id = 1;
|
nuclear@54
|
70 p->parent = 0; /* no parent for init */
|
nuclear@42
|
71
|
nuclear@55
|
72 p->ticks_left = TIMESLICE_TICKS;
|
nuclear@55
|
73 p->next = p->prev = 0;
|
nuclear@55
|
74
|
nuclear@55
|
75 /* the first process may keep this existing page table */
|
nuclear@55
|
76 p->ctx.pgtbl_paddr = get_pgdir_addr();
|
nuclear@55
|
77
|
nuclear@42
|
78 /* allocate a chunk of memory for the process image
|
nuclear@42
|
79 * and copy the code of test_proc there.
|
nuclear@42
|
80 */
|
nuclear@51
|
81 proc_size_pg = (test_proc_end - test_proc) / PGSIZE + 1;
|
nuclear@45
|
82 if((img_start_pg = pgalloc(proc_size_pg, MEM_USER)) == -1) {
|
nuclear@45
|
83 panic("failed to allocate space for the init process image\n");
|
nuclear@45
|
84 }
|
nuclear@54
|
85 img_start_addr = PAGE_TO_ADDR(img_start_pg);
|
nuclear@54
|
86 memcpy((void*)img_start_addr, test_proc, proc_size_pg * PGSIZE);
|
nuclear@54
|
87 printf("copied init process at: %x\n", img_start_addr);
|
nuclear@47
|
88
|
nuclear@47
|
89 /* allocate the first page of the process stack */
|
nuclear@47
|
90 stack_pg = ADDR_TO_PAGE(KMEM_START) - 1;
|
nuclear@47
|
91 if(pgalloc_vrange(stack_pg, 1) == -1) {
|
nuclear@47
|
92 panic("failed to allocate user stack page\n");
|
nuclear@47
|
93 }
|
nuclear@54
|
94 p->user_stack_pg = stack_pg;
|
nuclear@52
|
95
|
nuclear@54
|
96 /* allocate a kernel stack for this process */
|
nuclear@54
|
97 if((p->kern_stack_pg = pgalloc(KERN_STACK_SIZE / PGSIZE, MEM_KERNEL)) == -1) {
|
nuclear@54
|
98 panic("failed to allocate kernel stack for the init process\n");
|
nuclear@54
|
99 }
|
nuclear@54
|
100 /* when switching from user space to kernel space, the ss0:esp0 from TSS
|
nuclear@54
|
101 * will be used to switch to the per-process kernel stack, so we need to
|
nuclear@54
|
102 * set it correctly before switching to user space.
|
nuclear@54
|
103 * tss->ss0 is already set in init_proc above.
|
nuclear@54
|
104 */
|
nuclear@54
|
105 tss->esp0 = PAGE_TO_ADDR(p->kern_stack_pg) + KERN_STACK_SIZE;
|
nuclear@45
|
106
|
nuclear@45
|
107
|
nuclear@54
|
108 /* now we need to fill in the fake interrupt stack frame */
|
nuclear@54
|
109 memset(&ifrm, 0, sizeof ifrm);
|
nuclear@54
|
110 /* after the priviledge switch, this ss:esp will be used in userspace */
|
nuclear@54
|
111 ifrm.esp = PAGE_TO_ADDR(stack_pg) + PGSIZE;
|
nuclear@54
|
112 ifrm.ss = selector(SEGM_UDATA, 3);
|
nuclear@54
|
113 /* instruction pointer at the beginning of the process image */
|
nuclear@55
|
114 ifrm.eip = img_start_addr;
|
nuclear@54
|
115 ifrm.cs = selector(SEGM_UCODE, 3);
|
nuclear@54
|
116 /* make sure the user will run with interrupts enabled */
|
nuclear@54
|
117 ifrm.eflags = FLAGS_INTR_BIT;
|
nuclear@54
|
118 /* user data selectors should all be the same */
|
nuclear@54
|
119 ifrm.ds = ifrm.es = ifrm.fs = ifrm.gs = ifrm.ss;
|
nuclear@42
|
120
|
nuclear@51
|
121 /* add it to the scheduler queues */
|
nuclear@55
|
122 add_proc(p->id);
|
nuclear@55
|
123
|
nuclear@55
|
124 cur_pid = p->id; /* make it current */
|
nuclear@42
|
125
|
nuclear@54
|
126 /* execute a fake return from interrupt with the fake stack frame */
|
nuclear@54
|
127 intr_ret(ifrm);
|
nuclear@42
|
128 }
|
nuclear@42
|
129
|
nuclear@47
|
130
|
nuclear@47
|
131 void context_switch(int pid)
|
nuclear@42
|
132 {
|
nuclear@54
|
133 struct process *prev, *new;
|
nuclear@49
|
134
|
nuclear@55
|
135 assert(get_intr_state() == 0);
|
nuclear@55
|
136
|
nuclear@52
|
137 if(cur_pid == pid) {
|
nuclear@54
|
138 return; /* nothing to be done */
|
nuclear@52
|
139 }
|
nuclear@54
|
140 prev = proc + cur_pid;
|
nuclear@54
|
141 new = proc + pid;
|
nuclear@52
|
142
|
nuclear@54
|
143 /* push all registers onto the stack before switching stacks */
|
nuclear@54
|
144 push_regs();
|
nuclear@47
|
145
|
nuclear@54
|
146 prev->ctx.stack_ptr = switch_stack(new->ctx.stack_ptr);
|
nuclear@47
|
147
|
nuclear@54
|
148 /* restore registers from the new stack */
|
nuclear@54
|
149 pop_regs();
|
nuclear@47
|
150
|
nuclear@54
|
151 /* switch to the new process' address space */
|
nuclear@54
|
152 set_pgdir_addr(new->ctx.pgtbl_paddr);
|
nuclear@47
|
153
|
nuclear@54
|
154 /* make sure we'll return to the correct kernel stack next time
|
nuclear@54
|
155 * we enter from userspace
|
nuclear@54
|
156 */
|
nuclear@55
|
157 tss->esp0 = PAGE_TO_ADDR(new->kern_stack_pg) + KERN_STACK_SIZE;
|
nuclear@47
|
158 }
|
nuclear@51
|
159
|
nuclear@51
|
160 int get_current_pid(void)
|
nuclear@51
|
161 {
|
nuclear@51
|
162 return cur_pid;
|
nuclear@51
|
163 }
|
nuclear@51
|
164
|
nuclear@51
|
165 struct process *get_current_proc(void)
|
nuclear@51
|
166 {
|
nuclear@51
|
167 return cur_pid ? &proc[cur_pid] : 0;
|
nuclear@51
|
168 }
|
nuclear@51
|
169
|
nuclear@51
|
170 struct process *get_process(int pid)
|
nuclear@51
|
171 {
|
nuclear@51
|
172 return &proc[pid];
|
nuclear@51
|
173 }
|