View of xos/kernel/fork.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 "sched.h"
#include "proc.h"
#include "alloc_proc.h"
#include "idle.h"
#include "proc_struct.h"
 
#include <fd.h>
#include <resolve.h>
#include <node.h>
#include <stat_struct.h>
#include <vm.h>
#include <string_table.h>
#include <it_stack_structs.h>
#include <asm.h>
#include <errno.h>
#include <consts.h>
#include <enums.h>
#include <i386.h>
#include <stddef.h>
 
extern int end;
 
void _start_init();
 
static void init_file_descrs(struct file_descr_struct *file_descr_table[])
{
  struct file_descr_struct **file_descr;
 
  for (file_descr = file_descr_table; file_descr < file_descr_table + OPEN_MAX; file_descr++)
    *file_descr = NULL;
}
 
static void dup_file_descrs(struct file_descr_struct *const src[], struct file_descr_struct *dest[])
{
  struct file_descr_struct *const *file_descr1;
  struct file_descr_struct **file_descr2;
 
  file_descr2 = dest;
  for (file_descr1 = src; file_descr1 < src + OPEN_MAX; file_descr1++) {
    if ((*file_descr2 = *file_descr1))
      fd_dup(*file_descr2);
    file_descr2++;
  }
}
 
static void init_idle()
{
  static char idle_name[] = "<idle>\0";
  static const struct string_table_struct idle_cmdline = {1, idle_name, sizeof idle_name};
 
  proc_table_init();
  proc_list_init();
  proc_tree_init();
  proc_group_init();
  idle->state = PS_IDLE;
  idle->cmdline = idle_cmdline;
  sched_init();
}
 
/* Met en place le programme de init. */
static int setup_init(struct proc_struct *init)
{
  int retval;
 
  /* initialisation de la memoire virtuelle */
  if ((retval = vm_copy(NULL, &init->vm, init->page_directory)) < 0)
    goto error;
 
  /* texte + donnees */
  if ((retval = vm_map_mem(&init->vm, PAGE_SIZE, PAGE_ALIGN((unsigned long)&end))) < 0)
    goto error_vm_free;
  init->brk = (unsigned long)&end;
 
  /* pile */
  if (vm_map_anon(&init->vm, USER_STACK_TOP - PAGE_SIZE, PAGE_SIZE, PROT_RDWR, 1, "stack") < 0)
    goto error_vm_free;
 
  return 0;
 
 error_vm_free:
  vm_free(&init->vm);
 error:
  return retval;
}
 
static int init_init(struct proc_struct *init)
{
  int retval;
  int intr;
 
  disable_intr(intr);
  proc_table_add(init);
  proc_list_add(init);
  proc_tree_add(init, idle);
  if ((retval = proc_create_proc_group(init)) < 0)
    goto error_proc_tree_remove;
  init->state = PS_RUNNING;
  init->did_exec = 0;
  status_init(init);
  if ((retval = string_table_init_from_string(&init->cmdline, "<init>")) < 0)
    goto error_proc_group_remove;
  init->wd = root;
  node_hold(init->wd);
  if ((retval = setup_init(init)) < 0)
    goto error_release_wd;
  init_file_descrs(init->file_descr_table);
  signal_init(init, 1);
  sleep_init(init);
  sched_add(init);
  restore_intr(intr);
  return 0;
 
 error_release_wd:
  node_release(init->wd);
  string_table_destroy(&init->cmdline);
 error_proc_group_remove:
  proc_group_remove(init);
 error_proc_tree_remove:
  proc_tree_remove(init);
  proc_list_remove(init);
  proc_table_remove(init);
  restore_intr(intr);
  return retval;
}
 
static int clone_proc(struct proc_struct *child)
{
  int retval;
  int intr;
 
  disable_intr(intr);
  proc_table_add(child);
  proc_list_add(child);
  proc_tree_add(child, current);
  proc_group_add(child, current->group);
  child->state = PS_RUNNING;
  child->did_exec = 0;
  status_init(child);
  if ((retval = string_table_clone(&current->cmdline, &child->cmdline)) < 0)
    goto error_proc_group_remove;
  child->wd = current->wd;
  node_hold(child->wd);
  if ((retval = vm_copy(&current->vm, &child->vm, child->page_directory)) < 0)
    goto error_release_wd;
  child->brk = current->brk;
  dup_file_descrs(current->file_descr_table, child->file_descr_table);
  signal_init(child, 0);
  sleep_init(child);
  sched_add(child);
  restore_intr(intr);
  return 0;
 
 error_release_wd:
  node_release(child->wd);
  string_table_destroy(&child->cmdline);
 error_proc_group_remove:
  proc_group_remove(child);
  proc_tree_remove(child);
  proc_list_remove(child);
  proc_table_remove(child);
  restore_intr(intr);
  return retval;
}
 
void proc_init()
{
  alloc_proc_init();
  init_idle();
}
 
void proc_print_info()
{
  alloc_proc_print_info();
}
 
int spawn_init()
{
  int retval;
  struct registers_struct registers;
  struct interrupted_instruction_struct start_instruction;
  struct proc_struct *init;
 
  /* registres */
  /* eax n'est pas utilise par alloc_proc(). */
  registers.ebx = 0;
  registers.ecx = 0;
  registers.edx = 0;
  registers.esi = 0;
  registers.edi = 0;
  registers.ebp = 0;
  /* ds, es, fs et gs ne sont pas utilises par alloc_proc(). */
 
  /* instruction de depart */
  start_instruction.eip = (unsigned long)_start_init;
  /* cs n'est pas utilise par alloc_proc(). */
  start_instruction.eflags = 0x200;
  start_instruction.esp = USER_STACK_TOP;
  /* ss n'est pas utilise par alloc_proc(). */
 
  if ((retval = alloc_proc(&registers, &start_instruction, &init)) < 0)
    goto error;
  if ((retval = init_init(init)) < 0)
    goto error_free_init;
  return 0;
 
 error_free_init:
  free_proc(init);
 error:
  return retval;
}
 
int fork(const struct registers_struct *registers, const struct interrupted_instruction_struct *interrupted_instruction)
{
  int retval;
  struct proc_struct *child;
 
  if ((retval = alloc_proc(registers, interrupted_instruction, &child)) < 0)
    goto error;
  if ((retval = clone_proc(child)) < 0)
    goto error_free_proc;
  return child->id;
 
 error_free_proc:
  free_proc(child);
 error:
  return retval;
}
 
void change_set_proc_group(struct proc_group_struct *proc_group)
{
  int intr;
 
  disable_intr(intr);
  if (proc_group == current->group)
    goto restore;
  proc_change_set_proc_group(current, proc_group);
 restore:
  restore_intr(intr);
}
 
int change_create_proc_group()
{
  int intr;
  int retval;
 
  disable_intr(intr);
  retval = proc_change_create_proc_group(current);
  restore_intr(intr);
  return retval;
}
 
int change_set_child_proc_group(struct proc_struct *child, struct proc_group_struct *proc_group)
{
  int intr;
 
  if (child->did_exec)
    return -EACCES;
  disable_intr(intr);
  if (proc_group == child->group)
    goto restore;
  proc_change_set_proc_group(child, proc_group);
 restore:
  restore_intr(intr);
  return 0;
}
 
int change_create_child_proc_group(struct proc_struct *child)
{
  int intr;
  int retval;
 
  if (child->did_exec)
    return -EACCES;
  disable_intr(intr);
  retval = proc_change_create_proc_group(child);
  restore_intr(intr);
  return retval;
}
 
int change_wd(const char *fs_path)
{
  int retval;
  struct file_struct *file;
  struct stat_struct statbuf;
 
  /* resolution du nom et controle du type du fichier */
  if ((retval = resolve(fs_path, &file)) < 0)
    goto end;
  if ((retval = stat_node(file, &statbuf)) < 0)
    goto release;
  if (statbuf.type != FT_DIR) {
    retval = -ENOTDIR;
    goto release;
  }
 
  /* changement de repertoire de travail */
  node_release(current->wd);
  current->wd = file;
  node_hold(current->wd);
 
  retval = 0;
 release:
  node_release(file);
 end:
  return retval;
}