root/usr/src/cmd/modload/modinfo.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/types.h>
#include <inttypes.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/modctl.h>
#include <sys/errno.h>

static int wide;
static int count = 0;
static int first_mod = 1;

/*
 * When printing module load addresses on 32-bit kernels, the 8 hex
 * character field width is obviously adequate. On sparcv9 kernels
 * solely by virtue of the choice of code model enabled by the separate
 * kernel context, the text addresses are currently in the lower 4G
 * address range, and so -still- fit in 8 hex characters.
 *
 * However, amd64 kernels live at the top of the 64-bit address space, and
 * so as to be honest about the addresses (and since this is a tool for
 * humans to parse), we have to print out a 16 hex character address.
 *
 * We assume that we will print out all 16 hex characters on future
 * 64-bit kernel ports too.
 */
static const char header[] =
        " Id "
#if defined(_LP64) && !defined(__sparcv9)
        "        "
#endif
        "Loadaddr   Size Info Rev Module Name\n";

static char *cheader  =
        " Id    Loadcnt Module Name                            State\n";


static void usage();
static void print_info(struct modinfo *mi);
static void print_cinfo(struct modinfo *mi);

/*
 * These functions are in modsubr.c
 */
void fatal(char *fmt, ...);
void error(char *fmt, ...);

/*
 * Display information of all loaded modules
 */
int
main(int argc, char *argv[])
{
        struct modinfo modinfo;
        int info_all = 1;
        int id;
        int opt;

        id = -1;        /* assume we're getting all loaded modules */

        while ((opt = getopt(argc, argv, "i:wc")) != EOF) {
                switch (opt) {
                case 'i':
                        if (sscanf(optarg, "%d", &id) != 1)
                                fatal("Invalid id %s\n", optarg);
                        if (id == -1)
                                id = 0;
                        info_all = 0;
                        break;
                case 'w':
                        wide++;
                        break;
                case 'c':
                        count++;
                        break;
                case '?':
                default:
                        usage();
                        break;
                }
        }


        /*
         * Next id of -1 means we're getting info on all modules.
         */
        modinfo.mi_id = modinfo.mi_nextid = id;
        modinfo.mi_info = (info_all) ? MI_INFO_ALL : MI_INFO_ONE;

        if (count)
                modinfo.mi_info |= MI_INFO_CNT;

        do {
                /*
                 * Get module information.
                 * If modinfo.mi_nextid == -1, get info about the
                 * next installed module with id > "id."
                 * Otherwise, get info about the module with id == "id."
                 */
                if (modctl(MODINFO, id, &modinfo) < 0) {
                        if (!info_all)
                                error("can't get module information");
                        break;
                }

                if (first_mod) {
                        first_mod = 0;
                        (void) printf("%s", count ? cheader : header);
                }
                if (count)
                        print_cinfo(&modinfo);
                else
                        print_info(&modinfo);
                /*
                 * If we're getting info about all modules, the next one
                 * we want is the one with an id greater than this one.
                 */
                id = modinfo.mi_id;
        } while (info_all);

        return (0);
}

/*
 * Display loadcounts.
 */
static void
print_cinfo(struct modinfo *mi)
{
        (void) printf("%3d %10d %-32s", mi->mi_id, mi->mi_loadcnt, mi->mi_name);
        (void) printf(" %s/%s\n",
            mi->mi_state & MI_LOADED ? "LOADED" : "UNLOADED",
            mi->mi_state & MI_INSTALLED ? "INSTALLED" : "UNINSTALLED");
}

/*
 * Display info about a loaded module.
 *
 * The sparc kernel resides in its own address space, with modules
 * loaded at low addresses.  The low 32-bits of a module's base
 * address is sufficient but does put a cap at 4gb here.
 * The x86 64-bit kernel is loaded in high memory with the full
 * address provided.
 */
static void
print_info(struct modinfo *mi)
{
        int n, p0;
        char namebuf[256];

        for (n = 0; n < MODMAXLINK; n++) {
                if (n > 0 && mi->mi_msinfo[n].msi_linkinfo[0] == '\0')
                        break;

                (void) printf("%3d ", mi->mi_id);
#if defined(_LP64) && !defined(__sparcv9)
                (void) printf("%16lx ", (uintptr_t)mi->mi_base);
#elif defined(_LP64)
                (void) printf("%8lx ", (uintptr_t)mi->mi_base);
#else
                (void) printf("%8x ", (uintptr_t)mi->mi_base);
#endif
#if defined(_LP64)
                (void) printf("%6lx ", mi->mi_size);
#else
                (void) printf("%6x ", mi->mi_size);
#endif

                p0 = mi->mi_msinfo[n].msi_p0;

                if (p0 != -1)
                        (void) printf("%3d ", p0);
                else
                        (void) printf("  - ");

                (void) printf("  %d  ", mi->mi_rev);

                mi->mi_name[MODMAXNAMELEN - 1] = '\0';
                mi->mi_msinfo[n].msi_linkinfo[MODMAXNAMELEN - 1] = '\0';

                if (wide) {
                        (void) printf("%s (%s)\n", mi->mi_name,
                            mi->mi_msinfo[n].msi_linkinfo);
                } else {
                        /* snprintf(3c) will always append a null character */
                        (void) snprintf(namebuf, sizeof (namebuf), "%s (%s)",
                            mi->mi_name, mi->mi_msinfo[n].msi_linkinfo);
#if defined(_LP64) && !defined(__sparcv9)
                        (void) printf("%.43s\n", namebuf);
#else
                        (void) printf("%.51s\n", namebuf);
#endif
                }
        }
}

static void
usage()
{
        fatal("usage:  modinfo [-w] [-c] [-i module-id]\n");
}