View of xos/mm/page_fault.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 "maps.h"
#include "map.h"
#include "page.h"
#include "page_frame.h"
#include "highmem.h" /* is_highmem() */
 
#include <consts.h>
#include <enums.h>
#include <config.h>
#include <i386.h>
#include <assert.h>
#include <string.h>
 
#define copy_page(dest, src) memcpy((dest), (src), PAGE_SIZE)
 
/* Toutes les fonctions operent sur la traduction d'adresse du processus
 * courant. Le parametre page_frame est une adresse physique de cadre de page,
 * le parametre laddr est une adresse lineaire, non necessairement alignee sur
 * une frontiere de page, mais seule sa page est prise en compte. */
 
/* Cette fonction compte sur le fait que la page utilisateur 0 est non presente
 * pour y realiser une projection temporaire. */
static inline void copy_from_highmem(void *dest_page, unsigned long src_page_frame)
{
  set_page(USER_BASE, src_page_frame, 0);
  copy_page(dest_page, (void *)USER_BASE);
  del_page(USER_BASE);
}
 
static unsigned long get_page_frame(unsigned long laddr)
{
  unsigned long page_frame;
  assert(get_page_frame_addr(laddr, &page_frame));
  return page_frame;
}
 
/* Cree une nouvelle page et retourne son cadre de page. */
static unsigned long create_page(unsigned long laddr, int prot, const struct map_struct *map)
{
  unsigned long page_frame;
 
  if (!(page_frame = alloc_page_frame()))
    goto error;
  if (!set_page(laddr, page_frame, prot == PROT_RDWR))
    goto error_release_page_frame;
  if (!map_fill_page(map, PAGE(laddr)))
    goto error_del_page;
  if (prot == PROT_RDONLY)
    /* la page est propre, on l'enregistre */
    map_set_clean_page_frame(map, laddr, page_frame);
  return page_frame;
 
 error_del_page:
  del_page(laddr);
 error_release_page_frame:
  release_page_frame(page_frame);
 error:
  return 0;
}
 
/* Cree une page en lecture seule avec le cadre de page. */
static int put_page(unsigned long laddr, unsigned long page_frame)
{
  if (!set_page(laddr, page_frame, 0))
    return 0;
  return 1;
}
 
/* Donne la permission d'ecriture a une page. */
static void set_write_access(unsigned long laddr, const struct map_struct *map)
{
  map_dirty_page(map, laddr, get_page_frame(laddr)); /* la page devient sale */
  assert(set_page_rw(laddr, 1));
}
 
/* Cree une nouvelle page par copie, et lui donne la permission d'ecriture. */
static int copy_page_frame(unsigned long laddr, unsigned long page_frame)
{
  unsigned long new_page_frame;
 
  if (!(new_page_frame = alloc_page_frame()))
    goto error;
  if (!is_highmem(new_page_frame)) {
    if (is_highmem(page_frame))
      copy_from_highmem((void *)new_page_frame, page_frame);
    else
      copy_page((void *)new_page_frame, (void *)page_frame);
  }
  if (!set_page(laddr, new_page_frame, 1))
    goto error_release_new_page_frame;
  if (is_highmem(new_page_frame)) {
    if (is_highmem(page_frame))
      copy_from_highmem((void *)PAGE(laddr), page_frame);
    else
      copy_page((void *)PAGE(laddr), (void *)page_frame);
  }
  return 1;
 
 error_release_new_page_frame:
  release_page_frame(new_page_frame);
 error:
  return 0;
}
 
static int handle_not_present(unsigned long laddr, int access, const struct map_struct *map)
{
  unsigned long page_frame;
 
  page_frame = map_get_clean_page_frame(map, laddr);
  switch (access) {
  case PF_READ:
    if (page_frame) {
      /* on partage le cadre de page */
      hold_page_frame(page_frame);
      if (!put_page(laddr, page_frame))
        goto error_release_page_frame;
      goto _1;
    error_release_page_frame:
      release_page_frame(page_frame);
      return PF_ADRERR;
    }
    else {
      /* on cree la page */
      if (!(page_frame = create_page(laddr, PROT_RDONLY, map))) /* la page est copy-on-write */
        return PF_ADRERR;
    }
  _1:
    break;
  case PF_WRITE:
  default:
    if (page_frame) {
      /* on fait une copie privee de la page */
      if (!copy_page_frame(laddr, page_frame))
        return PF_ADRERR;
    }
    else {
      /* on cree la page */
      if (!create_page(laddr, PROT_RDWR, map))
        return PF_ADRERR;
    }
  }
  return 0;
}
 
static int handle_protection_violation(unsigned long laddr, const struct map_struct *map)
{
  unsigned long page_frame;
 
  page_frame = get_page_frame(laddr);
  if (is_page_frame_shared(page_frame)) {
    /* on fait une copie privee de la page */
    if (!copy_page_frame(laddr, page_frame))
      return PF_ADRERR;
    release_page_frame(page_frame);
  }
  else
    /* on donne la permission d'ecriture a la page */
    set_write_access(laddr, map);
  return 0;
}
 
int _handle_page_fault(unsigned long laddr, int cause, int access, int in_stack, const struct maps_struct *maps)
{
  struct map_struct *map;
 
  /* on cherche le segment */
  if (!(map = find_next_map(maps, laddr)))
    return PF_MAPERR;
  if (compare_map_laddr(map, laddr) == 0) {
    /* on verifie les permissions */
    if (!map_verify_access(map, access))
      return PF_ACCERR;
  }
  else {
    /* on verifie que la projection peut s'etendre vers le bas (pile) */
    if (!map_grows_down(map))
      return PF_MAPERR;
    /* on verifie qu'on est bien dans la pile */
    if (!in_stack)
      return PF_MAPERR;
    /* on verifie les permissions */
    if (!map_verify_access(map, access))
      return PF_ACCERR;
    /* on etend la projection */
    map_grow_down(map, laddr);
  }
 
  switch (cause) {
  case PF_NOT_PRESENT: /* not-present page */
    return handle_not_present(laddr, access, map);
  case PF_PROTECTION_VIOLATION: /* page-level protection violation */
  default:
    return handle_protection_violation(laddr, map);
  }
}