]> wirehaze git hosting - ppos.git/blob - ppos/kernel/task.c

wirehaze git hosting

tasks implementation (ongoing)
[ppos.git] / ppos / kernel / task.c
1 // PingPongOS - PingPong Operating System
2
3 /* PEDRO HENRIQUE FRIEDRICH RAMOS : GRR20243133 */
4
5 #include <sys/mman.h>
6 #include <stlib.h>
7 #include "tcb.h"
8 #include "ctx.h"
9
10 /* ------------------------------------------------------------------------- */
11 /* Constants -------------------------------------------------------------- */
12
13 /* 8 pages = 32 KiB */
14 #define STACK_SIZE (8 * 4096)
15
16 /* ------------------------------------------------------------------------- */
17 /* Static variables ------------------------------------------------------- */
18
19 static struct task_t *task_current;
20 static struct task_t task_kernel; /* Is task_kernel like init for ppos? */
21
22 /* ------------------------------------------------------------------------- */
23 /* Internal functions ----------------------------------------------------- */
24
25 /* Allocate memory for a task's stack.
26 *
27 * The size parameter must be page aligned! */
28 static inline void *
29 alloc_stack (size_t size)
30 {
31
32 /* Using mmap () instead of malloc () makes a little bit more sense, since
33 * when a task ends the mapping can be immediately deleted. If we use malloc,
34 * we can't know for sure if the brk will be lowered.
35 *
36 * Also, it (probably) makes the stack allocations farther apart, so it's less
37 * likely that a task can corrupt another task's stack by accident, which is
38 * possible since ppos doesn't have virtual memory nor memory protection. */
39
40 return mmap (/* Let the kernel choose the address for the mapping. */
41 NULL,
42
43 /* Size of the mapping (must be page-aligned). */
44 size,
45
46 /* RW stack. */
47 PROT_READ | PROT_WRITE,
48
49 /* Not shared between (linux) processes.
50 * Not backed by a file. */
51 MAP_PRIVATE | MAP_ANONYMOUS,
52
53 /* No file descriptor. */
54 -1,
55
56 /* No offset. */
57 0);
58 }
59
60 /* ------------------------------------------------------------------------- */
61
62 /* Free allocated memory for a task's stack. */
63 static inline int
64 free_stack (void *stack, size_t size)
65 {
66 return munmap (stack, size);
67 }
68
69 /* ------------------------------------------------------------------------- */
70
71 /* Get the next free task id.
72 *
73 * Stupid simple implementation, not thread safe! */
74 static int
75 get_next_id (void)
76 {
77 static int id = 0;
78 return ++id; /* id=0 is reserved for task_kernel */
79 }
80
81 /* ------------------------------------------------------------------------- */
82 /* Exported functions ----------------------------------------------------- */
83
84 void
85 task_init (void)
86 {
87 /* TODO */
88 }
89
90 /* ------------------------------------------------------------------------- */
91
92 struct task_t *
93 task_create (char *name, void (*entry) (void *), void *arg)
94 {
95 struct task_t *task;
96 unsigned char *stack;
97
98 if (!(task = malloc (sizeof *task)))
99 goto err_malloc_task;
100
101 if (!(stack = alloc_stack (STACK_SIZE)))
102 goto err_malloc_stack;
103
104 task->id = get_next_id ();
105 task->name = name;
106 task->status = STATUS_READY;
107
108 /* Probably not the best way to do this but with this interface and the
109 * current implementation it's the best we can do right now.
110 *
111 * We need it for task_switch ().
112 *
113 * In a multiprocessor system this doesn't hold up. */
114
115 task->parent = task_current;
116
117 if (ctx_create (&task->context, entry, arg, stack, STACK_SIZE))
118 goto err_ctx_create;
119
120 return task;
121
122 err_ctx_create:
123 free_stack (stack, STACK_SIZE);
124 err_malloc_stack:
125 free (task);
126 err_malloc_task:
127 return NULL;
128 }
129
130 /* ------------------------------------------------------------------------- */
131
132 int
133 task_destroy (struct task_t *task)
134 {
135 if (task->status != STATUS_TERMINATED)
136 return ERROR;
137
138 if (free_stack (task->context.stack, task->context.size))
139 return ERROR; /* Probably EINVAL. */
140
141 free (task);
142
143 return NOERROR;
144 }
145
146 /* ------------------------------------------------------------------------- */
147
148 int
149 task_switch (struct task_t *task)
150 {
151 if (!task)
152 return task_switch (task_current->parent);
153
154 /* Instructions not clear... */
155 switch (task->status)
156 {
157 case STATUS_TERMINATED: /* Switch to task_kernel or to parent? */
158 return task_switch (task_kernel);
159
160 case STATUS_RUNNING: /* Return ERROR or switch to another task? */
161 return ERROR;
162 }
163
164 task_current->status = STATUS_SUSPENDED;
165 task_current = task;
166 task_current->status = STATUS_RUNNING;
167
168 /* task_current and task are not the same, because if they task->status would
169 * be STATUS_RUNNING, which is handled earlier.
170 *
171 * Since that's the only error case for ctx_swap (), we're fine ... */
172
173 return ctx_swap (&task_current->context, &task->context);
174 }
175
176 /* ------------------------------------------------------------------------- */
177
178 int
179 task_id (struct task_t *task)
180 {
181 if (!task)
182 return task_id (task->current);
183
184 return task->id;
185 }
186
187 /* ------------------------------------------------------------------------- */
188
189 char *
190 task_name (struct task_t *task)
191 {
192 if (!task)
193 return task_id (task->current);
194
195 return task->name;
196 }
197
198 /* ------------------------------------------------------------------------- */