root/usr/src/lib/cfgadm_plugins/ac/common/mema_prom.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 (c) 1996-1998 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#include <stddef.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/param.h>
#include <sys/obpdefs.h>
#include <sys/fhc.h>
#include <sys/ac.h>
#include <sys/sysctrl.h>
#include <sys/openpromio.h>
#include "mema_prom.h"
#include <config_admin.h>


/*
 * PROM access routines to get and set disabled lists
 * Based on code in the usr/src/cmd/eeprom directory.
 */
#define PROMDEV         "/dev/openprom"
/*
 * 128 is the size of the largest (currently) property name
 * 8192 - MAXPROPSIZE - sizeof (int) is the size of the largest
 * (currently) property value, viz. nvramrc.
 * the sizeof(u_int) is from struct openpromio
 */

#define MAXPROPSIZE     128
#define MAXNAMESIZE     MAXPROPSIZE
#define MAXVALSIZE      (8192 - MAXPROPSIZE - sizeof (u_int))
#define BUFSIZE         (MAXPROPSIZE + MAXVALSIZE + sizeof (u_int))
typedef union {
        char buf[BUFSIZE];
        struct openpromio opp;
} Oppbuf;
#define PROP_MEMORY_LIST        "disabled-memory-list"

static int prom_read_one(mema_disabled_t *, int, int, char *, u_int);
static int prom_write_one(mema_disabled_t *, int, int, char *, u_int);

int
prom_read_disabled_list(mema_disabled_t *dp, int bd)
{
        int prom_fd;
        int ret;

        (void) memset((void *)dp, 0, sizeof (*dp));
        prom_fd = open(PROMDEV, O_RDONLY);
        if (prom_fd == -1) {
                return (0);
        }
        ret = prom_read_one(dp, bd, prom_fd,
            PROP_MEMORY_LIST, PROM_MEMORY_DISABLED);
        (void) close(prom_fd);
        return (ret);
}

int
prom_write_disabled_list(mema_disabled_t *dp, int bd)
{
        int prom_fd;
        int ret;

        prom_fd = open(PROMDEV, O_RDWR);
        if (prom_fd == -1) {
                return (0);
        }
        ret = prom_write_one(dp, bd, prom_fd,
            PROP_MEMORY_LIST, PROM_MEMORY_DISABLED);
        (void) close(prom_fd);
        return (ret);
}

static int
prom_read_one(
        mema_disabled_t *dp,
        int bd,
        int prom_fd,
        char *var,
        u_int bit)
{
        Oppbuf oppbuf;
        struct openpromio *opp = &oppbuf.opp;
        int ret;

        (void) memset((void *)&oppbuf, 0, sizeof (oppbuf));
        (void) strncpy(opp->oprom_array, var, MAXNAMESIZE);
        opp->oprom_size = MAXVALSIZE;
        if (ioctl(prom_fd, OPROMGETOPT, opp) == -1) {
                ret = 0;
        } else
        if (opp->oprom_size == 0) {
                /* Not a failure - just not set to anything */
                ret = 1;
        } else {
                char *cp;
                int board;

                ret = 1;
                for (cp = opp->oprom_array; *cp != '\0'; cp++) {
                        switch (*cp) {
                        case '0':
                        case '1':
                        case '2':
                        case '3':
                        case '4':
                        case '5':
                        case '6':
                        case '7':
                        case '8':
                        case '9':
                                board = *cp - '0';
                                break;
                        case 'a':
                        case 'b':
                        case 'c':
                        case 'd':
                        case 'e':
                        case 'f':
                                board = *cp - 'a' + 10;
                                break;
                        case 'A':
                        case 'B':
                        case 'C':
                        case 'D':
                        case 'E':
                        case 'F':
                                board = *cp - 'A' + 10;
                                break;
                        default:
                                /* Ignore bad characters. */
                                /* TODO: maybe should set ret to 0? */
                                board = -1;
                                break;
                        }
                        if (board == bd)
                                *dp |= bit;
                }
        }
        return (ret);
}

static int
prom_write_one(
        mema_disabled_t *dp,
        int bd,
        int prom_fd,
        char *var,
        u_int bit)
{
        Oppbuf in_oppbuf;
        struct openpromio *in_opp = &in_oppbuf.opp;
        Oppbuf oppbuf;
        struct openpromio *opp = &oppbuf.opp;
        int ret;
        char *cp;

        /* Setup output buffer. */
        (void) memset((void *)&oppbuf, 0, sizeof (oppbuf));
        (void) strncpy(opp->oprom_array, var, MAXNAMESIZE);
        opp->oprom_size = strlen(var) + 1;
        cp = opp->oprom_array + opp->oprom_size;

        /*
         * First read the existing list, filtering out 'bd' if 'bit'
         * not set.
         */
        (void) memset((void *)&in_oppbuf, 0, sizeof (in_oppbuf));
        (void) strncpy(in_opp->oprom_array, var, MAXNAMESIZE);
        in_opp->oprom_size = MAXVALSIZE;
        if (ioctl(prom_fd, OPROMGETOPT, in_opp) != -1 &&
            in_opp->oprom_size != 0) {
                char *icp;
                int board;

                for (icp = in_opp->oprom_array; *icp != '\0'; icp++) {
                        switch (*icp) {
                        case '0': case '1': case '2': case '3':
                        case '4': case '5': case '6': case '7':
                        case '8': case '9':
                                board = *icp - '0';
                                break;
                        case 'a': case 'b': case 'c':
                        case 'd': case 'e': case 'f':
                                board = *icp - 'a' + 10;
                                break;
                        case 'A': case 'B': case 'C':
                        case 'D': case 'E': case 'F':
                                board = *icp - 'A' + 10;
                                break;
                        default:
                                /* Ignore bad characters. */
                                continue;
                        }
                        /* If enabling this board ... */
                        if (board == bd && (*dp & bit) == 0)
                                continue;
                        *cp++ = "0123456789abcdef"[board];
                        opp->oprom_size++;
                }
        }

        if ((*dp & bit) != 0) {
                *cp++ = "0123456789abcdef"[bd];
                opp->oprom_size++;
        }
        if (ioctl(prom_fd, OPROMSETOPT, opp) == -1) {
                ret = 0;
        } else {
                ret = 1;
        }

        return (ret);
}

/*
 * The PROM only has board-level disable of memory.  If two banks are present
 * on the board, both are either enabled or disabled at boot.
 * The caller of this routine must set the PROM_MEMORY_PRESENT bits
 * before calling this function.
 */

/*ARGSUSED*/
int
prom_viable_disabled_list(mema_disabled_t *dp)
{
#ifdef  XXX
        int board;

        for (board = 0; board < MAX_BOARDS; board++) {
                if ((dp->bank_A[board] & PROM_MEMORY_PRESENT) != 0 &&
                    (dp->bank_B[board] & PROM_MEMORY_PRESENT) != 0 &&
                    (dp->bank_A[board] & PROM_MEMORY_DISABLED) !=
                    (dp->bank_B[board] & PROM_MEMORY_DISABLED)) {
                        return (0);
                }
        }
#endif
        return (1);
}