/* Carte de la memoire lineaire utilisateur d'un processus */ /* 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 "map.h" #include "map_struct.h" #include #include #include #include #include #include #include #include static inline int check_for_addition(unsigned long i1, unsigned long i2) { return i1 <= ULONG_MAX - i2; } static int merge_map(struct maps_struct *maps, struct map_struct *map, struct map_struct *map1) { if (!map_merge(map, map1)) return 0; if (map1->next) { map->next = map1->next; map1->next->previous = map; } else { map->next = NULL; maps->last_map = map; } return 1; } /* Construit une carte de la memoire vide. */ void init_maps(struct maps_struct *maps) { maps->first_map = NULL; maps->last_map = NULL; } /* Retourne 1 en cas de succes, 0 en cas d'echec. */ int insert_map(struct maps_struct *maps, struct map_struct *map) { struct map_struct *map1, *map2; int st; map1 = NULL; map2 = maps->first_map; while (map2) { st = compare_maps(map, map2); if (st == 0) /* les maps se chevauchent */ return 0; if (st < 0) break; map1 = map2; map2 = map2->next; } /* insertion de map entre map1 et map2 */ if (!map2) { map->next = NULL; maps->last_map = map; } else if (!merge_map(maps, map, map2)) { map->next = map2; map2->previous = map; } if (!map1) { map->previous = NULL; maps->first_map = map; } else if (!merge_map(maps, map1, map)) { map->previous = map1; map1->next = map; } return 1; } /* En cas d'erreur, l'effacement peut etre partiellement realise. */ int clear_maps(struct maps_struct *maps, unsigned long start, unsigned long length) { struct map_struct *map1, *map2, *new_map, *next_map; int st; int rv; map1 = NULL; map2 = maps->first_map; /* recherche de la premiere map apres le debut de l'intervalle */ while (1) { if (!map2) return 0; st = compare_map_laddr(map2, start); if (st > 0) /* map2 est apres start */ break; if (st == 0) { /* map2 contient start */ if ((rv = map_split(map2, start, &new_map)) < 0) /* Hum. Situation bien embarrassante... */ return rv; if (new_map) { /* start est strictement inclus dans map2 : new_map creee */ new_map->previous = map2; new_map->next = map2->next; map2->next = new_map; if (new_map->next) new_map->next->previous = new_map; else maps->last_map = new_map; map1 = map2; map2 = new_map; } break; } map1 = map2; map2 = map2->next; } /* map1 est la derniere map avant le debut de l'intervalle, map2 est la premiere map apres le debut de l'intervalle (forcement non nulle). */ /* effacement des maps dans l'intervalle */ do { if (check_for_addition(start, length)) { st = compare_map_laddr(map2, start + length); if (st > 0) /* map2 est strictement apres start + length */ break; /* on s'arrete la */ if (st == 0) { /* map2 contient start + length */ /* map2 est a supprimer partiellement */ if ((rv = map_split(map2, start + length, &new_map)) < 0) return rv; if (!new_map) break; if (map2->next) { new_map->next = map2->next; new_map->next->previous = new_map; } else { new_map->next = NULL; maps->last_map = new_map; } if (map1) { new_map->previous = map1; map1->next = new_map; } else { new_map->previous = NULL; maps->first_map = new_map; } free_map(map2); break; } } /* map2 est a supprimer entierement */ if (map1) { if (map2->next) { map1->next = map2->next; map2->next->previous = map1; } else { map1->next = NULL; maps->last_map = map1; } } else { if (map2->next) { maps->first_map = map2->next; map2->next->previous = NULL; } else { maps->first_map = NULL; maps->last_map = NULL; } } next_map = map2->next; free_map(map2); map2 = next_map; } while (map2); return 0; } /* dest_maps doit etre initialise et vide dans l'intervalle demande. * En cas d'erreur, un code d'erreur est renvoye, et les maps deja clonees dans * dest_maps ne sont pas detruites. */ int clone_maps(const struct maps_struct *maps, struct maps_struct *dest_maps, unsigned long start, unsigned long length) { struct map_struct *map, *new_map; int st; int rv; for (map = maps->first_map; map; map = map->next) { if ((rv = map_clone(map, start, length, &st, &new_map)) < 0) return rv; /* erreur */ if (st > 0) /* map apres l'intervalle */ break; /* on s'arrete la */ if (st == 0) /* map dans l'intervalle : clonage effectue */ assert(insert_map(dest_maps, new_map)); } return 0; } int protect_maps(struct maps_struct *maps, unsigned long start, unsigned long length, int prot) { struct map_struct *map, *new_map; int st; int rv; map = maps->first_map; /* recherche de la premiere map apres le debut de l'intervalle */ while (1) { if (!map) return 0; st = compare_map_laddr(map, start); if (st > 0) /* map est apres start */ break; if (st == 0) { /* map contient start */ if (compare_map_prot(map, prot)) { map = map->next; break; } if ((rv = map_split(map, start, &new_map)) < 0) /* Hum. Situation bien embarrassante... */ return rv; if (new_map) { /* start est strictement inclus dans map : new_map creee */ new_map->previous = map; new_map->next = map->next; map->next = new_map; if (new_map->next) new_map->next->previous = new_map; else maps->last_map = new_map; map = new_map; } break; } map = map->next; } /* map est la premiere map apres le debut de l'intervalle (forcement non nulle). */ /* protection des maps dans l'intervalle */ do { if (check_for_addition(start, length)) { st = compare_map_laddr(map, start + length); if (st > 0) /* map est strictement apres start + length */ break; /* on s'arrete la */ if (st == 0) { /* map contient start + length */ /* map est a proteger partiellement */ if (compare_map_prot(map, prot)) break; if ((rv = map_split(map, start + length, &new_map)) < 0) return rv; if (!new_map) break; if (map->next) { new_map->next = map->next; new_map->next->previous = new_map; } else { new_map->next = NULL; maps->last_map = new_map; } new_map->previous = map; map->next = new_map; map_protect(map, prot); break; } } /* map est a proteger entierement */ map_protect(map, prot); map = map->next; } while (map); return 0; } struct map_struct *find_map(const struct maps_struct *maps, unsigned long laddr) { struct map_struct *map; for (map = maps->first_map; map; map = map->next) if (compare_map_laddr(map, laddr) == 0) return map; return NULL; } struct map_struct *find_next_map(const struct maps_struct *maps, unsigned long laddr) { struct map_struct *map; for (map = maps->first_map; map; map = map->next) if (compare_map_laddr(map, laddr) >= 0) return map; /* map est la map qui contient laddr ou celle immediatement superieure */ return NULL; } /* Retourne l'adresse de demarrage d'une zone libre pour une projection de * longueur length dans le segment [base, base + size[ de l'espace * d'adressage lineaire du processus courant. * base doit etre aligne sur une frontiere de page. * size doit etre non nul et multiple de PAGE_SIZE. * [base, base + size[ doit etre valide. * length doit etre non nul et multiple de PAGE_SIZE. * Si side est negatif, la zone libre est calee vers les basses addresses, * sinon la zone libre est calee vers les hautes addresses. * Zero est retourne en cas d'echec. */ unsigned long get_free_area(const struct maps_struct *maps, unsigned long base, unsigned long size, unsigned long length, int side) { const struct map_struct *map1, *map2; unsigned long start, len; int st; if (length > size) return 0; if (side < 0) { map1 = NULL; map2 = maps->first_map; while (1) { st = hole_intersect(map1, map2, base, size, &start, &len); if (st > 0) return 0; if (st == 0 && len >= length) return start; if (!map2) return 0; map1 = map2; map2 = map2->next; } } else { map1 = maps->last_map; map2 = NULL; while (1) { st = hole_intersect(map1, map2, base, size, &start, &len); if (st < 0) return 0; if (st == 0 && len >= length) return start + (len - length); if (!map1) return 0; map2 = map1; map1 = map1->previous; } } } /* Retourne la liste des projections. */ int get_maps(const struct maps_struct *maps, struct map_info_struct **map_info_array, unsigned int *count) { int retval; int map_count; struct map_struct *map; int i, j; if (!maps->first_map) { *map_info_array = NULL; *count = 0; return 0; } map_count = 0; for (map = maps->first_map; map; map = map->next) map_count++; if (!(*map_info_array = kmalloc(map_count * sizeof (struct map_info_struct)))) return -ENOMEM; *count = map_count; i = 0; for (map = maps->first_map; map; map = map->next) { if ((retval = map_get_info(map, &(*map_info_array)[i])) < 0) goto error; i++; } return 0; error: for (j = 0; j < i; j++) if ((*map_info_array)[j].pathname) kfree((*map_info_array)[j].pathname, strlen((*map_info_array)[j].pathname) + 1); kfree(*map_info_array, *count * sizeof (struct map_info_struct)); return retval; }