View of xos/kernel/alloc_proc.c


XOS | Parent Directory | View | Download

/* 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 <http://www.gnu.org/licenses/>. */
 
#include "proc_struct.h"
 
#include <kmalloc.h>
#include <printk.h>
#include <gdt.h>
#include <system.h>
#include <system_data_structs.h>
#include <it_stack_structs.h>
#include <asm.h>
#include <errno.h>
#include <i386.h>
#include <stddef.h>
 
/* 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));
}