root/usr/src/psm/promif/ieee1275/common/prom_io.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 (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
 */

#include <sys/promif.h>
#include <sys/promimpl.h>

/*
 *  Returns 0 on error. Otherwise returns a handle.
 */
int
prom_open(char *path)
{
        cell_t ci[5];
        promif_owrap_t *ow;
#ifdef PROM_32BIT_ADDRS
        char *opath = NULL;
        size_t len;

        if ((uintptr_t)path > (uint32_t)-1) {
                opath = path;
                len = prom_strlen(opath) + 1; /* include terminating NUL */
                path = promplat_alloc(len);
                if (path == NULL)
                        return (0);
                (void) prom_strcpy(path, opath);
        }
#endif

        ow = promif_preout();
        promif_preprom();
        ci[0] = p1275_ptr2cell("open");         /* Service name */
        ci[1] = (cell_t)1;                      /* #argument cells */
        ci[2] = (cell_t)1;                      /* #result cells */
        ci[3] = p1275_ptr2cell(path);           /* Arg1: Pathname */
        ci[4] = (cell_t)0;                      /* Res1: Prime result */

        (void) p1275_cif_handler(&ci);

        promif_postprom();
        promif_postout(ow);

#ifdef PROM_32BIT_ADDRS
        if (opath != NULL)
                promplat_free(path, len);
#endif

        return (p1275_cell2int(ci[4]));         /* Res1: ihandle */
}


int
prom_seek(int fd, unsigned long long offset)
{
        cell_t ci[7];

        ci[0] = p1275_ptr2cell("seek");         /* Service name */
        ci[1] = (cell_t)3;                      /* #argument cells */
        ci[2] = (cell_t)1;                      /* #result cells */
        ci[3] = p1275_uint2cell((uint_t)fd);    /* Arg1: ihandle */
        ci[4] = p1275_ull2cell_high(offset);    /* Arg2: pos.hi */
        ci[5] = p1275_ull2cell_low(offset);     /* Arg3: pos.lo */
        ci[6] = (cell_t)-1;                     /* Res1: Prime result */

        promif_preprom();
        (void) p1275_cif_handler(&ci);
        promif_postprom();

        return (p1275_cell2int(ci[6]));         /* Res1: actual */
}

/*ARGSUSED3*/
ssize_t
prom_read(ihandle_t fd, caddr_t buf, size_t len, uint_t startblk, char devtype)
{
        cell_t ci[7];
        promif_owrap_t *ow;
#ifdef PROM_32BIT_ADDRS
        caddr_t obuf = NULL;

        if ((uintptr_t)buf > (uint32_t)-1) {
                obuf = buf;
                buf = promplat_alloc(len);
                if (buf == NULL)
                        return (-1);
        }
#endif

        ow = promif_preout();
        promif_preprom();

        ci[0] = p1275_ptr2cell("read");         /* Service name */
        ci[1] = (cell_t)3;                      /* #argument cells */
        ci[2] = (cell_t)1;                      /* #result cells */
        ci[3] = p1275_size2cell((uint_t)fd);    /* Arg1: ihandle */
        ci[4] = p1275_ptr2cell(buf);            /* Arg2: buffer address */
        ci[5] = p1275_uint2cell(len);           /* Arg3: buffer length */
        ci[6] = (cell_t)-1;                     /* Res1: Prime result */

        (void) p1275_cif_handler(&ci);

        promif_postprom();
        promif_postout(ow);

#ifdef PROM_32BIT_ADDRS
        if (obuf != NULL) {
                promplat_bcopy(buf, obuf, len);
                promplat_free(buf, len);
        }
#endif

        return (p1275_cell2size(ci[6]));        /* Res1: actual length */
}

/*
 * prom_write is the only prom_*() function we have to intercept
 * because all the other prom_*() io interfaces eventually call
 * into prom_write().
 */
/*ARGSUSED3*/
ssize_t
prom_write(ihandle_t fd, caddr_t buf, size_t len, uint_t startblk, char devtype)
{
        cell_t ci[7];
        promif_owrap_t *ow;
        ssize_t rlen;

#ifdef PROM_32BIT_ADDRS
        caddr_t obuf = NULL;
        static char smallbuf[256];

        ASSERT(buf);
#endif

        /*
         * If the callback address is set, attempt to redirect
         * console output back into kernel terminal emulator.
         */
        if (promif_redirect != NULL && fd == prom_stdout_ihandle()) {
                ow = promif_preout();
                rlen = promif_redirect(promif_redirect_arg, (uchar_t *)buf,
                    len);
                promif_postout(ow);
                return (rlen);
        }

#ifdef PROM_32BIT_ADDRS
        if ((uintptr_t)buf > (uint32_t)-1) {
                /*
                 * This is a hack for kernel message output.
                 * By avoiding calls to promplat_alloc (and
                 * using smallbuf instead) when memory is low
                 * we can print shortish kernel messages without
                 * deadlocking. smallbuf should be at least as
                 * large as the automatic buffer in
                 * prom_printf.c:_doprint()'s stack frame.
                 * promplat_alloc() can block on a mutex and so
                 * is called here before calling promif_preprom().
                 */
                if (len > sizeof (smallbuf)) {
                        obuf = buf;
                        buf = promplat_alloc(len);
                        if (buf == NULL) {
                                return (-1);
                        }
                        promplat_bcopy(obuf, buf, len);
                }
        }
#endif

        /*
         * Normally we'd call promif_preprom() just before
         * calling into the prom (to enforce single-threaded
         * access) but here we need to call it before accessing
         * smallbuf, since smallbuf is statically allocated and
         * hence can only be accessed by one thread at a time.
         */
        ow = promif_preout();
        promif_preprom();

#ifdef PROM_32BIT_ADDRS
        if ((uintptr_t)buf > (uint32_t)-1) {
                /*
                 * If buf is small enough, use smallbuf
                 * instead of promplat_alloc() (see above)
                 * smallbuf is static, so single thread
                 * access to it by using it only after
                 * promif_preprom()
                 */
                if (len <= sizeof (smallbuf)) {
                        promplat_bcopy(buf, smallbuf, len);
                        buf = smallbuf;
                }
        }
#endif

        ci[0] = p1275_ptr2cell("write");        /* Service name */
        ci[1] = (cell_t)3;                      /* #argument cells */
        ci[2] = (cell_t)1;                      /* #result cells */
        ci[3] = p1275_uint2cell((uint_t)fd);    /* Arg1: ihandle */
        ci[4] = p1275_ptr2cell(buf);            /* Arg2: buffer addr */
        ci[5] = p1275_size2cell(len);           /* Arg3: buffer len */
        ci[6] = (cell_t)-1;                     /* Res1: Prime result */

        (void) p1275_cif_handler(&ci);
        rlen = p1275_cell2size(ci[6]);          /* Res1: actual len */

        promif_postprom();
        promif_postout(ow);

#ifdef PROM_32BIT_ADDRS
        if (obuf != NULL)
                promplat_free(buf, len);
#endif

        return (rlen);
}

int
prom_close(int fd)
{
        cell_t ci[4];
        promif_owrap_t *ow;

        ci[0] = p1275_ptr2cell("close");        /* Service name */
        ci[1] = (cell_t)1;                      /* #argument cells */
        ci[2] = (cell_t)0;                      /* #result cells */
        ci[3] = p1275_uint2cell((uint_t)fd);    /* Arg1: ihandle */

        ow = promif_preout();
        promif_preprom();
        (void) p1275_cif_handler(&ci);
        promif_postprom();
        promif_postout(ow);

        return (0);
}