/*
* 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);
}