root/usr/src/cmd/rcap/rcapd/rcapd_mapping.c
/*
 * 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.
 */

#include <sys/types.h>
#include <assert.h>
#include <stdlib.h>
#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 */