/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include "rcapd_mapping.h" #include "utils.h" /* * lmapping_t is a list of non-overlapping mappings, ordered by address. These * functions add, remove, and verify the existence of mappings in such a list. * rcapd_scanner.c is a consumer. */ typedef struct lmapping_find_cb_arg { uintptr_t lmfa_addr; size_t lmfa_size; lmapping_t *lmfa_prior; lmapping_t *lmfa_ret; } lmapping_find_cb_arg_t; #ifdef DEBUG /* * Verify a sublist is properly ordered. */ static void lmapping_verify(lmapping_t *lm) { while (lm != NULL) { if (lm->lm_next != NULL) ASSERT(lm->lm_next->lm_addr > lm->lm_addr); lm = lm->lm_next; } } #else /* !DEBUG */ #define lmapping_verify(x) ((void)0) #endif /* DEBUG */ /* * Determine the position of a mapping with the given address and size. Upon * return, lmfa_ret will be set to the actual mapping, if it exists, and * lmfa_prior will be set to the mapping which does or would precede one with * the given characteristics. */ static int lmapping_find_cb(lmapping_t *lm, void *arg) { lmapping_find_cb_arg_t *lmfa = arg; if (lm->lm_addr >= lmfa->lmfa_addr) { if (lmfa->lmfa_addr == lm->lm_addr && lmfa->lmfa_size == lm->lm_size) lmfa->lmfa_ret = lm; return (1); } else lmfa->lmfa_prior = lm; return (0); } static void lmapping_walk(lmapping_t *lm, int(*lmapping_walk_cb)(lmapping_t *, void *), void *arg) { lmapping_t *next; while (lm != NULL) { next = lm->lm_next; lmapping_verify(lm); if (lmapping_walk_cb(lm, arg) != 0) { lmapping_verify(lm); return; } lm = next; } } int lmapping_remove(lmapping_t **lm, uintptr_t addr, size_t size) { lmapping_find_cb_arg_t lmfa; lmfa.lmfa_addr = addr; lmfa.lmfa_size = size; lmfa.lmfa_prior = lmfa.lmfa_ret = NULL; lmapping_verify(*lm); lmapping_walk(*lm, lmapping_find_cb, &lmfa); if (lmfa.lmfa_ret == NULL) return (-1); if (lmfa.lmfa_prior != NULL) lmfa.lmfa_prior->lm_next = lmfa.lmfa_ret->lm_next; else if (*lm == lmfa.lmfa_ret) *lm = lmfa.lmfa_ret->lm_next; free(lmfa.lmfa_ret); lmapping_verify(*lm); return (0); } int lmapping_insert(lmapping_t **lm, uintptr_t addr, size_t size) { lmapping_find_cb_arg_t lmfa; lmapping_t *cur; cur = malloc(sizeof (*cur)); if (cur == NULL) return (-1); cur->lm_addr = addr; cur->lm_size = size; cur->lm_next = NULL; lmfa.lmfa_addr = addr; lmfa.lmfa_size = size; lmfa.lmfa_prior = lmfa.lmfa_ret = NULL; lmapping_verify(*lm); lmapping_walk(*lm, lmapping_find_cb, &lmfa); ASSERT(lmfa.lmfa_ret == NULL); if (lmfa.lmfa_prior != NULL) { cur->lm_next = lmfa.lmfa_prior->lm_next; lmfa.lmfa_prior->lm_next = cur; } else { cur->lm_next = *lm; *lm = cur; } lmapping_verify(*lm); return (0); } int lmapping_contains(lmapping_t *lm, uintptr_t addr, size_t size) { lmapping_find_cb_arg_t lmfa; lmfa.lmfa_addr = addr; lmfa.lmfa_size = size; lmfa.lmfa_ret = NULL; lmapping_walk(lm, lmapping_find_cb, &lmfa); return (lmfa.lmfa_ret != NULL); } /*ARGSUSED*/ static int lmapping_free_cb(lmapping_t *lm, void *arg) { free(lm); return (0); } void lmapping_free(lmapping_t **lm) { lmapping_walk(*lm, lmapping_free_cb, NULL); *lm = NULL; } #ifdef DEBUG int lmapping_dump_diff(lmapping_t *lm1, lmapping_t *lm2) { lmapping_t **lmv; int res = 0; int ch = 0; int label_printed = 0; #define OUTPUT_LABEL() \ if (label_printed == 0) { \ debug("changes in mappings:\n"); \ label_printed++; \ } while (lm1 != NULL && lm2 != NULL) { if ((lm1->lm_addr != lm2->lm_addr) || (lm1->lm_size != lm2->lm_size)) { res = -1; if (lm1->lm_addr == lm2->lm_addr && lm1->lm_size < lm2->lm_size || lm1->lm_addr < lm2->lm_addr) { lmv = &lm1; ch = '-'; } else { lmv = &lm2; ch = '+'; } OUTPUT_LABEL(); debug("%c%p+0x%llx\n", ch, (void *)(*lmv)->lm_addr, (long long)(*lmv)->lm_size); *lmv = (*lmv)->lm_next; } else { lm1 = lm1->lm_next; lm2 = lm2->lm_next; } } while (lm1 != NULL) { OUTPUT_LABEL(); debug("%c%p+0x%llx\n", '-', (void *)lm1->lm_addr, (unsigned long long)lm1->lm_size); lm1 = lm1->lm_next; res = 1; } while (lm2 != NULL) { OUTPUT_LABEL(); debug("%c%p+0x%llx\n", '+', (void *)lm2->lm_addr, (long long)lm2->lm_size); lm2 = lm2->lm_next; res = 1; } return (res); #undef OUTPUT_LABEL } #endif /* DEBUG */