UnnamedOS
task.c
Go to the documentation of this file.
1 
13 #include <common.h>
14 #include <tasks/task.h>
15 #include <tasks/schedule.h>
16 #include <tasks/elf.h>
17 #include <interrupts/isr.h>
18 #include <mem/gdt.h>
19 #include <mem/mmu.h>
20 #include <mem/vmm.h>
21 #include <boot/multiboot.h>
22 #include <string.h>
23 
24 #define MAX_TASKS 1024
25 
26 static task_t* tasks[MAX_TASKS];
27 
33 static task_t* task_get(task_pid_t pid) {
34  if (!tasks[pid]) {
35  println("%4aTask %d does not exist%a", pid);
36  return 0;
37  }
38  return tasks[pid];
39 }
40 
47  task_pid_t pid;
48  // pid 0 is an error value
49  for (pid = 1; tasks[pid] && pid < MAX_TASKS; pid++);
50  if (pid == MAX_TASKS)
51  panic("Maximum task number reached");
52  tasks[pid] = task;
53  return pid;
54 }
55 
60 static void task_remove(task_pid_t pid) {
61  tasks[pid] = 0;
62 }
63 
75 static task_pid_t task_create_detailed(void* entry_point,
76  page_directory_t* page_directory, size_t kernel_stack_len,
77  size_t user_stack_len, elf_t* elf, size_t code_segment, size_t data_segment) {
79  logln("TASK", "Creating task with %dKB kernel and %dKB user stack",
80  kernel_stack_len, user_stack_len);
81  // Here we allocate a whole page (4KB) which is more than we need.
82  // TODO: use a proper heap (malloc)
83  task_t* task = vmm_alloc(sizeof(task_t), VMM_KERNEL);
84  task->page_directory = page_directory ? page_directory :
87  task->state = TASK_RUNNING;
88  task->vm86 = 0;
89  task->elf = elf;
90  task->kernel_stack = vmm_alloc(kernel_stack_len, VMM_KERNEL);
91  task->user_stack = vmm_alloc(user_stack_len, VMM_USER | VMM_WRITABLE);
92  task->kernel_stack_len = kernel_stack_len;
93  task->user_stack_len = user_stack_len;
96  cpu_state_t* cpu = task->cpu = (cpu_state_t*)
97  (task->kernel_stack + kernel_stack_len - 1 - sizeof(cpu_state_t));
99  cpu->gs = cpu->fs = cpu->es = cpu->ds = gdt_get_selector(data_segment);
100  // We don't need to set ESP because it is discarded by popa. Instead it is
101  // set to the pointer value of cpu itself, so the TOS of the kernel stack.
102  cpu->r.edi = cpu->r.esi = cpu->r.ebp = cpu->r.ebx =
103  cpu->r.edx = cpu->r.ecx = cpu->r.eax = 0;
104  // we also ignore intr and error, those are always set when entering the kernel
105  cpu->eip = (uintptr_t) entry_point;
106  cpu->cs = gdt_get_selector(code_segment);
107  cpu->eflags.dword = 0; // first reset EFLAGS, then
108  cpu->eflags.bits._if = 1; // enable interrupts, otherwise
109  cpu->eflags.bits.reserved = 1; // we can't exit the task once entered!
110  // If we want to go to userspace, the following registers are popped by iret.
111  // Note that if we stay in the kernel, these values are ignored.
112  cpu->user_esp = (uint32_t) task->user_stack + user_stack_len - 1;
113  cpu->user_ss = gdt_get_selector(data_segment);
114  // The VM86 values will be ignored so we don't need to set them.
116  task_pid_t pid = task_add(task);
117  isr_enable_interrupts(old_interrupts);
118  return pid;
119 }
120 
129  size_t kernel_stack_len) {
130  return task_create_detailed(entry_point, page_directory, kernel_stack_len,
132 }
133 
144  size_t kernel_stack_len, size_t user_stack_len, void* elf) {
145  return task_create_detailed(entry_point, page_directory, kernel_stack_len,
146  user_stack_len, elf, GDT_RING3_CODE_SEG, GDT_RING3_DATA_SEG);
147 }
148 
154  task_get(pid)->state = TASK_STOPPED;
155 }
156 
163  if (task_get(pid)->state == TASK_RUNNING) {
164  println("%4aYou may not destroy a running task%a");
165  return;
166  }
167  logln("TASK", "Destroying task %d", pid);
168  task_t* task = task_get(pid);
170  vmm_free(task->kernel_stack, task->kernel_stack_len);
171  vmm_free(task->user_stack, task->user_stack_len);
174  vmm_free(task, sizeof(task_t));
175  task_remove(pid);
176  isr_enable_interrupts(old_interrupts);
177 }
178 
185  for (pid++; pid < MAX_TASKS && !tasks[pid]; pid++);
186  if (pid == MAX_TASKS) {
187  for (pid = 1; pid < MAX_TASKS && !tasks[pid]; pid++);
188  if (pid == MAX_TASKS)
189  return 0;
190  }
191  return pid;
192 }
193 
201  uint32_t pids = 0;
202  do {
203  pid = task_get_next_task(pid);
204  if (!pid)
205  return 0;
206  pids++;
207  } while (pids <= MAX_TASKS && task_get(pid)->state != state);
208  if (pids > MAX_TASKS)
209  return 0;
210  return pid;
211 }
212 
219  return task_get(pid)->ticks;
220 }
221 
228 uint32_t task_set_ticks(task_pid_t pid, uint32_t ticks) {
229  task_t* task = task_get(pid);
230  uint32_t old_ticks = task->ticks;
231  task->ticks = ticks;
232  return old_ticks;
233 }
234 
241  return task_get(pid)->cpu;
242 }
243 
250  task_get(pid)->cpu = cpu;
251 }
252 
259  return task_get(pid)->page_directory;
260 }
261 
267 uint8_t task_get_vm86(task_pid_t pid) {
268  return task_get(pid)->vm86;
269 }
270 
277  return task_get(pid)->elf;
278 }
279 
283 void task_dump() {
284  task_pid_t initial_pid = task_get_next_task(0), pid = initial_pid;
285  logln("TASK", "Task list:");
286  if (!initial_pid) {
287  logln("TASK", "There are no tasks.");
288  return;
289  }
290  do {
291  logln("TASK", "%s task with pid %d%s",
292  task_get(pid)->state ? "Running" : "Stopped", pid,
293  task_get(pid)->vm86 ? " (VM86)" : "");
294  } while ((pid = task_get_next_task(pid)) && pid != initial_pid);
295 }
296 
#define GDT_RING3_DATA_SEG
user data segment index
Definition: gdt.h:16
page_directory_t * vmm_create_page_directory()
Creates an empty page directory.
Definition: vmm.c:72
uint8_t task_get_vm86(task_pid_t pid)
Returns whether a task is a VM86 task.
Definition: task.c:267
uint8_t isr_enable_interrupts(uint8_t enable)
Enables or disables interrupts.
Definition: isr.c:42
void * elf
if this is an ELF task, this points to the ELF file
Definition: task.h:35
task_pid_t task_get_next_task(task_pid_t pid)
Returns the next task from the task list.
Definition: task.c:184
uint16_t gs
GS segment register.
Definition: isr.h:60
task_state_t state
whether the task is running or stopped
Definition: task.h:26
task_pid_t task_create_kernel(void *entry_point, page_directory_t *page_directory, size_t kernel_stack_len)
Creates a kernel task.
Definition: task.c:128
task_stack_t * kernel_stack
stack for handling interrupts
Definition: task.h:28
uint32_t edx
data register
Definition: isr.h:43
static task_pid_t task_create_detailed(void *entry_point, page_directory_t *page_directory, size_t kernel_stack_len, size_t user_stack_len, elf_t *elf, size_t code_segment, size_t data_segment)
Creates a task.
Definition: task.c:75
static page_directory_t * page_directory
the current page directory
Definition: vmm.c:44
uint32_t ticks
how many ticks the task may run per time slice
Definition: task.h:33
void vmm_modified_page_directory()
Ends a page directory modification.
Definition: vmm.c:164
The CPU&#39;s state when an interrupt occurs.
Definition: isr.h:58
void * vmm_alloc(size_t len, vmm_flags_t flags)
Marks some page(s) as used and maps them somewhere into memory.
Definition: vmm.c:511
task_pid_t task_create_user(void *entry_point, page_directory_t *page_directory, size_t kernel_stack_len, size_t user_stack_len, void *elf)
Creates a user task.
Definition: task.c:143
#define GDT_RING3_CODE_SEG
user code segment index
Definition: gdt.h:15
#define GDT_RING0_DATA_SEG
kernel data segment index
Definition: gdt.h:14
task_pid_t task_add(task_t *task)
Adds a new task to the task list and associates a PID.
Definition: task.c:46
cpu_state_t * cpu
saved CPU state when entering/leaving interrupts
Definition: task.h:32
size_t kernel_stack_len
kernel stack length
Definition: task.h:30
size_t user_stack_len
user stack length
Definition: task.h:30
static uint8_t old_interrupts
for temporary modifications
Definition: vmm.c:46
void task_set_cpu(task_pid_t pid, cpu_state_t *cpu)
Sets a task&#39;s CPU state.
Definition: task.c:249
uint32_t user_ss
stack segment (only pushed and popped in user space)
Definition: isr.h:74
cpu_state_t * task_get_cpu(task_pid_t pid)
Returns a task&#39;s CPU state.
Definition: task.c:240
uint8_t _if
interrupt flag
Definition: isr.h:19
page_directory_t * page_directory
this task&#39;s virtual memory map
Definition: task.h:27
uint32_t eip
the instruction to return to after the interrupt
Definition: isr.h:68
uint32_t task_pid_t
unique process ID
Definition: task.h:17
internal representation of a task
Definition: task.h:25
task_pid_t task_get_next_task_with_state(task_pid_t pid, task_state_t state)
Returns the next task from the task list with a specified state.
Definition: task.c:200
void task_dump()
Dumps the task list.
Definition: task.c:283
uint16_t gdt_get_selector(size_t entry)
Returns a selector ready to be loaded in a segment register.
Definition: gdt.c:103
uint16_t es
ES segment register.
Definition: isr.h:60
uint8_t vm86
whether this task is running in Virtual 8086 mode
Definition: task.h:34
void vmm_free(void *vaddr, size_t len)
Frees the given page(s) and unmaps them from memory.
Definition: vmm.c:528
uint32_t dword
useful for casting
Definition: isr.h:38
uint32_t esi
source index register
Definition: isr.h:43
void task_stop(task_pid_t pid)
Stops a task.
Definition: task.c:153
void vmm_destroy_page_directory(page_directory_t *dir_phys)
Destroys a page directory.
Definition: vmm.c:90
#define MAX_TASKS
maximum number of tasks
Definition: task.c:24
task_stack_t * user_stack
stack for the actual task&#39;s code
Definition: task.h:29
An entry in a page directory.
Definition: vmm.h:25
static task_t * tasks[MAX_TASKS]
Array of tasks.
Definition: task.c:26
uint32_t ebp
base pointer register
Definition: isr.h:43
uint16_t fs
FS segment register.
Definition: isr.h:60
uint8_t reserved
always one
Definition: isr.h:19
void vmm_modify_page_directory(page_directory_t *new_directory)
Loads a page directory for temporary modification.
Definition: vmm.c:149
struct isr_eflags_t::@8 bits
bit field
uint32_t eax
accumulator register
Definition: isr.h:43
uint16_t cs
the code segment to return to after the interrupt
Definition: isr.h:71
page_directory_t * task_get_page_directory(task_pid_t pid)
Returns a task&#39;s page directory.
Definition: task.c:258
task_state_t
state of a task
Definition: task.h:20
uint16_t isr_eflags_t eflags
the EFLAGS register before the interrupt was fired
Definition: isr.h:71
void * task_get_elf(task_pid_t pid)
Returns a task&#39;s ELF file.
Definition: task.c:276
task_state_t task_get_ticks(task_pid_t pid)
Returns a task&#39;s number of remaining ticks.
Definition: task.c:218
uint32_t ebx
base register
Definition: isr.h:43
static task_t * task_get(task_pid_t pid)
Returns the internal task structure associated with the given PID.
Definition: task.c:33
void task_destroy(task_pid_t pid)
Destroys a task.
Definition: task.c:161
uint32_t edi
destination index register
Definition: isr.h:43
uint32_t user_esp
stack pointer (only pushed and popped in user space)
Definition: isr.h:74
The ELF header at the start of every ELF file.
Definition: elf.h:15
uint16_t ds
DS segment register.
Definition: isr.h:60
uint32_t ecx
counter register
Definition: isr.h:43
static void task_remove(task_pid_t pid)
Removes a task from the task list.
Definition: task.c:60
uint32_t task_set_ticks(task_pid_t pid, uint32_t ticks)
Sets a task&#39;s number of remaining ticks.
Definition: task.c:228
uint16_t isr_registers_t r
The general purpose registers.
Definition: isr.h:60
#define GDT_RING0_CODE_SEG
kernel code segment index
Definition: gdt.h:13