/* Copyright (C) 2008 Emmanuel Varoquaux
This file is part of XOS.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
#include "proc_struct.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* table des taches */
static struct task_table_entry {
struct task_table_entry *next_free;
} task_table[NR_TASKS];
static struct task_table_entry *first_free;
/* processus inactif */
static struct proc_struct _idle;
struct proc_struct *const idle = &_idle;
/* Table des taches */
static void init_task_table()
{
struct task_table_entry *entry;
/* task_table[0] n'est jamais libre : c'est idle. */
entry = first_free = task_table + 1;
while (entry != task_table + (NR_TASKS - 1)) {
entry->next_free = entry + 1;
entry++;
}
entry->next_free = NULL;
}
static void print_task_table_info()
{
printk("Task table size: %d\n", NR_TASKS);
}
static unsigned int alloc_task_nr()
{
struct task_table_entry *entry;
if (!(entry = first_free))
return 0;
first_free = first_free->next_free;
return entry - task_table;
}
static void free_task_nr(unsigned int task_nr)
{
struct task_table_entry *entry;
entry = task_table + task_nr;
entry->next_free = first_free;
first_free = entry;
}
/* Taches */
static void init_idle_task()
{
struct tss_data_struct tss_data;
/* Inutile de definir idle->kernel_stack. */
/* table des pages de premier niveau */
get_pdbr(idle->page_directory);
/* TSS */
tss_data.pdbr = (unsigned long)idle->page_directory;
tss_data.ldt = 0;
tss_data.ss0 = 0; /* idle ne fait jamais de changement de privilege donc il est inutile de definir ss0 et esp0. */
tss_data.esp0 = 0;
tss_data.ss1 = 0;
tss_data.esp1 = 0;
tss_data.ss2 = 0;
tss_data.esp2 = 0;
tss_data.trap = 0;
tss_data.iomap_base = 0;
/* Inutile de definir les champs dynamiques car idle est en cours d'execution. */
set_tss(&idle->tss, &tss_data);
gdt_set_tss_descr(0, &idle->tss);
/* selecteur de TSS */
idle->tss_sel = get_tss_sel(0);
/* chargement des registres systeme */
lldt(0);
ltr(idle->tss_sel);
}
static int init_task(struct proc_struct *proc, const struct registers_struct *registers, const struct interrupted_instruction_struct *start_instruction)
{
int retval;
unsigned int task_nr;
struct tss_data_struct tss_data;
/* allocation d'un numero de tache */
if (!(task_nr = alloc_task_nr())) {
retval = -EAGAIN;
goto error;
}
/* pile noyau */
if (!(proc->kernel_stack = kmalloc(KERNEL_STACK_SIZE))) {
retval = -ENOMEM;
goto error_free_task;
}
/* table des pages de premier niveau */
if (!(proc->page_directory = kmalloc(PAGE_SIZE))) {
retval = -ENOMEM;
goto error_free_kernel_stack;
}
/* TSS */
tss_data.pdbr = (unsigned long)proc->page_directory;
tss_data.ldt = 0;
tss_data.ss0 = KERNEL_DATA_SEL;
tss_data.esp0 = (unsigned long)(proc->kernel_stack + KERNEL_STACK_SIZE);
tss_data.ss1 = 0;
tss_data.esp1 = 0;
tss_data.ss2 = 0;
tss_data.esp2 = 0;
tss_data.trap = 0;
tss_data.iomap_base = sizeof (struct tss_struct); /* pas de ports autorises en mode utilisateur */
tss_data.eax = 0;
tss_data.ebx = registers->ebx;
tss_data.ecx = registers->ecx;
tss_data.edx = registers->edx;
tss_data.ebp = registers->ebp;
tss_data.esp = start_instruction->esp;
tss_data.esi = registers->esi;
tss_data.edi = registers->edi;
tss_data.cs = USER_CODE_SEL;
tss_data.ds = USER_DATA_SEL;
tss_data.es = USER_DATA_SEL;
tss_data.fs = USER_DATA_SEL;
tss_data.gs = USER_DATA_SEL;
tss_data.ss = USER_DATA_SEL;
tss_data.eflags = start_instruction->eflags;
tss_data.eip = start_instruction->eip;
tss_data.previous = 0;
set_tss(&proc->tss, &tss_data);
gdt_set_tss_descr(task_nr, &proc->tss);
/* selecteur de TSS */
proc->tss_sel = get_tss_sel(task_nr);
return 0;
error_free_kernel_stack:
kfree(proc->kernel_stack, KERNEL_STACK_SIZE);
error_free_task:
free_task_nr(task_nr);
error:
return retval;
}
static void destroy_task(struct proc_struct *proc)
{
unsigned int task_nr;
/* numero de tache */
task_nr = get_task_nr(proc->tss_sel);
/* table des pages de premier niveau */
kfree(proc->page_directory, PAGE_SIZE);
/* pile noyau */
kfree(proc->kernel_stack, KERNEL_STACK_SIZE);
/* liberation du numero de tache */
free_task_nr(task_nr);
}
/* Interface publique */
void alloc_proc_init()
{
init_task_table();
init_idle_task();
}
void alloc_proc_print_info()
{
print_task_table_info();
}
/* Alloue et initialise une tache. Retourne un bloc de controle de processus
* non initialise sur cette tache. */
int alloc_proc(const struct registers_struct *registers, const struct interrupted_instruction_struct *start_instruction, struct proc_struct **proc)
{
int retval;
/* allocation de memoire pour le bloc de controle */
if (!(*proc = kmalloc(sizeof (struct proc_struct)))) {
retval = -ENOMEM;
goto error;
}
/* initialisation de la tache */
if ((retval = init_task(*proc, registers, start_instruction)) < 0)
goto error_kfree;
return 0;
error_kfree:
kfree(*proc, sizeof (struct proc_struct));
error:
return retval;
}
/* Detruit et libere une tache. */
void free_proc(struct proc_struct *proc)
{
/* destruction de la tache */
destroy_task(proc);
/* liberation de la memoire allouee pour le bloc de controle */
kfree(proc, sizeof (struct proc_struct));
}