/* 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 "maps.h" #include "map.h" #include "page.h" #include "page_frame.h" #include "highmem.h" /* is_highmem() */ #include #include #include #include #include #include #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); } }