/*
* 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
* 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 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Ported from 4.1.1_PSRA: "@(#)openprom.c 1.19 91/02/19 SMI";
*
* Porting notes:
*
* OPROMU2P unsupported after SunOS 4.x.
*
* Only one of these devices per system is allowed.
*/
/*
*/
#include <sys/openpromio.h>
#include <sys/autoconf.h>
#include <sys/wanboot_impl.h>
#include <sys/consplat.h>
#include <sys/bootconf.h>
#include <sys/bootprops.h>
/*
* XXX Make this dynamic.. or (better still) make the interface stateless
*/
static struct oprom_state {
void **result);
/* help functions */
opromopen, /* open */
opromclose, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
opromioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* prop_op */
NULL, /* streamtab */
};
DEVO_REV, /* devo_rev, */
0, /* refcnt */
opinfo, /* info */
nulldev, /* identify */
nulldev, /* probe */
opattach, /* attach */
opdetach, /* detach */
nodev, /* reset */
&openeepr_cb_ops, /* driver operations */
NULL, /* bus operations */
NULL, /* power */
ddi_quiesce_not_needed, /* quiesce */
};
/*
* Module linkage information for the kernel.
*/
};
&modldrv,
};
int
_init(void)
{
int error;
if (error != 0) {
return (error);
}
return (0);
}
int
{
}
int
_fini(void)
{
int error;
if (error != 0)
return (error);
return (0);
}
/*ARGSUSED*/
static int
{
switch (infocmd) {
case DDI_INFO_DEVT2DEVINFO:
error = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2INSTANCE:
/* All dev_t's map to the same, single instance */
*result = (void *)0;
error = DDI_SUCCESS;
break;
default:
break;
}
return (error);
}
static int
{
switch (cmd) {
case DDI_ATTACH:
if (prom_is_openprom()) {
} else {
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
{
if (cmd != DDI_DETACH)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* Allow multiple opens by tweaking the dev_t such that it looks like each
* open is getting a different minor device. Each minor gets a separate
* entry in the oprom_state[] table.
*/
/*ARGSUSED*/
static int
{
int m;
return (ENXIO);
for (m = 0; m < MAX_OPENS; m++)
if (st->already_open)
st++;
else {
/*
* It's ours.
*/
break;
}
if (m == MAX_OPENS) {
/*
* "Thank you for calling, but all our lines are
* busy at the moment.."
*
* We could get sophisticated here, and go into a
* sleep-retry loop .. but hey, I just can't see
* that many processes sitting in this driver.
*
* (And if it does become possible, then we should
* change the interface so that the 'state' is held
* external to the driver)
*/
return (EAGAIN);
}
return (0);
}
/*ARGSUSED*/
static int
{
}
st->already_open = 0;
return (0);
}
#ifdef __sparc
static int
{
if (root_is_ramdisk) {
return (-1);
} else {
if (BOP_GETPROP(bootops,
return (-1);
}
strlen(BP_ISCSI_DISK)) == 0) {
}
}
return (0);
}
#endif
struct opromioctl_args {
int cmd;
int mode;
};
/*ARGSUSED*/
static int
{
int cmd;
int mode;
int valsize;
char *valbuf;
int error = 0;
if (has_changed) {
/*
* The prom tree has changed since we last used current_id,
* so we need to check it.
*/
}
}
/*
* Check permissions
* and weed out unsupported commands on x86 platform
*/
switch (cmd) {
case OPROMLISTKEYSLEN:
break;
case OPROMLISTKEYS:
return (EFAULT);
if (valsize > userbufsize)
return (EINVAL);
return (EFAULT);
}
break;
case OPROMEXPORT:
return (EFAULT);
if (valsize > userbufsize)
return (EINVAL);
return (EFAULT);
}
break;
case OPROMEXPORTLEN:
break;
#endif
case OPROMGETOPT:
case OPROMNXTOPT:
return (EPERM);
}
break;
case OPROMSETOPT:
case OPROMSETOPT2:
break;
}
#endif /* !__i386 && !__amd64 */
return (EPERM);
case OPROMNEXT:
case OPROMCHILD:
case OPROMGETPROP:
case OPROMGETPROPLEN:
case OPROMNXTPROP:
case OPROMSETNODEID:
return (EPERM);
}
break;
case OPROMCOPYOUT:
return (EINVAL);
/*FALLTHROUGH*/
case OPROMSNAPSHOT:
case OPROMGETCONS:
case OPROMGETBOOTARGS:
case OPROMGETBOOTPATH:
case OPROMGETVERSION:
case OPROMPATH2DRV:
case OPROMPROM2DEVNAME:
case OPROMGETFBNAME:
case OPROMDEV2PROMNAME:
case OPROMREADY64:
#endif /* !__i386 && !__amd64 */
return (EPERM);
}
break;
case WANBOOT_SETKEY:
return (EPERM);
break;
#endif /* !__i386 && !defined(__amd64) */
default:
return (EINVAL);
}
/*
* Deal with SNAPSHOT and COPYOUT ioctls first
*/
switch (cmd) {
case OPROMCOPYOUT:
case OPROMSNAPSHOT:
}
/*
* Copy in user argument length and allocation memory
*
* NB do not copyin the entire buffer we may not need
* to. userbufsize can be as big as 32 K.
*/
return (EFAULT);
return (EINVAL);
/*
* Execute command
*/
switch (cmd) {
case OPROMGETOPT:
case OPROMGETPROP:
case OPROMGETPROPLEN:
if ((prom_is_openprom() == 0) ||
break;
}
/*
* The argument, a NULL terminated string, is a prop name.
*/
break;
}
/*
* 4010173: 'name' is a property, but not an option.
*/
valsize = -1;
if (cmd == OPROMGETPROPLEN) {
if (userbufsize < sizeof (int)) {
break;
}
opp->oprom_array);
if (valsize < userbufsize)
++valsize; /* Forces NULL termination */
/* If space permits */
} else {
/*
* XXX: There is no error code if the buf is too small.
* which is consistent with the current behavior.
*
* NB: This clause also handles the non-error
* zero length (boolean) property value case.
*/
opp->oprom_size = 0;
valsize = 1;
}
break;
case OPROMNXTOPT:
case OPROMNXTPROP:
if ((prom_is_openprom() == 0) ||
break;
}
/*
* The argument, a NULL terminated string, is a prop name.
*/
break;
}
propname);
/*
* 4010173: 'name' is a property, but it's not an option.
*/
propname);
}
if (valsize == 0) {
opp->oprom_size = 0;
} else if (++valsize <= userbufsize) {
}
break;
case OPROMNEXT:
case OPROMCHILD:
case OPROMSETNODEID:
if (prom_is_openprom() == 0 ||
userbufsize < sizeof (pnode_t)) {
break;
}
/*
* The argument is a phandle. (aka pnode_t)
*/
break;
}
/*
* If pnode_t from userland is garbage, we
* could confuse the PROM.
*/
(int)node_id);
break;
}
else if (cmd == OPROMCHILD)
else {
/* OPROMSETNODEID */
break;
}
break;
case OPROMGETCONS:
/*
* Is openboot supported on this machine?
* This ioctl used to return the console device,
* information; this is now done via modctl()
* in libdevinfo.
*/
opp->oprom_size = sizeof (char);
OPROMCONS_OPENPROM : 0;
/*
* The rest of the info is needed by Install to
* decide if graphics should be started.
*/
if ((getzoneid() == GLOBAL_ZONEID) &&
}
if ((getzoneid() == GLOBAL_ZONEID) &&
}
sizeof (char) + sizeof (uint_t)) != 0)
break;
case OPROMGETBOOTARGS: {
extern char kern_bootargs[];
if (valsize > userbufsize) {
break;
}
break;
}
case OPROMGETBOOTPATH: {
if (get_bootpath_prop(bpath) != 0) {
break;
}
if (valsize > userbufsize) {
break;
}
extern char saved_cmdline[];
if (valsize > userbufsize) {
break;
}
#endif
break;
}
/*
* convert a prom device path to an equivalent devfs path
*/
case OPROMPROM2DEVNAME: {
char *dev_name;
/*
* The input argument, a pathname, is a NULL terminated string.
*/
break;
}
if (error != 0) {
break;
}
if (++valsize > userbufsize) {
break;
}
break;
}
/*
* Convert a prom device path name to a driver name
*/
case OPROMPATH2DRV: {
char *drv_name;
/*
* The input argument, a pathname, is a NULL terminated string.
*/
break;
}
/*
* convert path to a driver binding name
*/
if (maj == DDI_MAJOR_T_NONE) {
break;
}
/*
* resolve any aliases
*/
break;
}
break;
}
case OPROMGETVERSION:
/*
* Get a string representing the running version of the
* prom. How to create such a string is platform dependent,
* so we just defer to a promif function. If no such
* association exists, the promif implementation
* may copy the string "unknown" into the given buffer,
* and return its length (incl. NULL terminator).
*
* We expect prom_version_name to return the actual
* length of the string, but copy at most userbufsize
* bytes into the given buffer, including NULL termination.
*/
if (valsize < 0) {
break;
}
/*
* copyout only the part of the user buffer we need to.
*/
sizeof (uint_t))) != 0)
break;
case OPROMGETFBNAME:
/*
* Return stdoutpath, if it's a frame buffer.
* Yes, we are comparing a possibly longer string against
* the size we're really going to copy, but so what?
*/
if ((getzoneid() == GLOBAL_ZONEID) &&
(prom_stdout_is_framebuffer() != 0) &&
} else
break;
/*
* Convert a logical or physical device path to prom device path
*/
case OPROMDEV2PROMNAME: {
char *prom_name;
/*
* The input argument, a pathname, is a NULL terminated string.
*/
break;
}
/*
* convert the devfs path to an equivalent prom path
*/
if (error != 0) {
break;
}
break;
}
}
break;
}
case OPROMSETOPT:
case OPROMSETOPT2: {
int namebuflen;
int valbuflen;
if ((prom_is_openprom() == 0) ||
break;
}
/*
* The arguments are a property name and a value.
* Copy in the entire user buffer.
*/
break;
}
/*
* The property name is the first string, value second
*/
if (cmd == OPROMSETOPT) {
} else {
break;
}
}
/*
* 4010173: 'name' is not an option, but it is a property.
*/
break;
}
case OPROMREADY64: {
int i;
if (userbufsize < sizeof (*opr)) {
break;
}
valsize = userbufsize -
opr->return_code = i;
/*
* copyout only the part of the user buffer we need to.
*/
sizeof (uint_t))) != 0)
break;
} /* case OPROMREADY64 */
case WANBOOT_SETKEY: {
int reslen;
int status;
int rv;
int i;
/*
* The argument is a struct wankeyio. Validate it as best
* we can.
*/
if (userbufsize != (sizeof (struct wankeyio))) {
break;
}
break;
}
/* check for key name and key size overflow */
for (i = 0; i < WANBOOT_MAXKEYNAMELEN; i++)
break;
if ((i == WANBOOT_MAXKEYNAMELEN) ||
break;
}
if (rv)
else
switch (status) {
case 0:
error = 0;
break;
case -2: /* out of key storage space */
break;
case -3: /* key name or value too long */
break;
case -4: /* can't delete: no such key */
break;
case -1: /* unspecified error */
default: /* this should not happen */
break;
}
break;
} /* case WANBOOT_SETKEY */
#endif /* !__i386 && !__amd64 */
} /* switch (cmd) */
return (error);
}
/*ARGSUSED*/
static int
{
return (ENXIO);
}
/*
* Copyin string and verify the actual string length is less than maxsize
* specified by the caller.
*
* Currently, maxsize is either OBP_MAXPROPNAME for property names
* or MAXPATHLEN for device path names. userbufsize is specified
* by the userland caller.
*/
static int
{
int error;
return (error);
}
return (EINVAL);
}
return (0);
}
/*
* Check pnode_t passed in from userland
*/
static int
{
int depth;
/*
* optimized path
*/
if (node_id == 0) {
return (1);
}
if (node_id == OBP_BADNODE) {
return (0);
}
return (1);
}
/*
* long path: walk from root till we find node_id
*/
depth = 1;
while (depth) {
return (1); /* node_id found */
depth++;
continue;
}
while (depth &&
depth--;
}
return (0); /* node_id not found */
}
static int
{
return (oprom_copynode(
}
static int
{
return (EBUSY);
/* copyin flag and create snapshot */
return (EFAULT);
}
/* copyout the size of the snapshot */
return (EFAULT);
}
return (0);
}
static int
{
int error = 0;
return (EBUSY);
/* copyin size and copyout snapshot */
if (error) {
/*
* on error keep the snapshot until a successful
* copyout or when the driver is closed.
*/
return (error);
}
return (0);
}
/*
* Copy all properties of nodeid into a single packed nvlist
*/
static int
{
int proplen;
/*
* non verbose mode, get the "name" property only
*/
if (flag == 0) {
if (proplen <= 0) {
"failed to get the name of openprom node 0x%x",
nodeid);
return (0);
}
return (0);
}
/*
* Ask for first property by passing a NULL string
*/
buf1[0] = '\0';
break; /* end of prop list */
if (proplen == 0) {
/* boolean property */
continue;
}
/* add 1 for null termination in case of a string */
}
return (0);
}
/*
* Copy all children and descendents into a a packed nvlist
*/
static int
{
if (child == 0)
return (0);
while (child != 0) {
return (-1);
}
}
return (0);
}
/*
* Copy a node into a packed nvlist
*/
static int
{
int error = 0;
/* @nodeid -- @ is not a legal char in a 1275 property name */
/* properties */
goto fail;
/* children */
if (error != 0)
goto fail;
}
/* pack into contiguous buffer */
fail:
return (error);
}
/*
* The driver is stateful across OPROMSNAPSHOT and OPROMCOPYOUT.
* This function encapsulates the state machine:
*
* -> IOC_IDLE -> IOC_SNAP -> IOC_DONE -> IOC_COPY ->
* | SNAPSHOT COPYOUT |
* --------------------------------------------------
*
* Returns 0 on success and -1 on failure
*/
static int
{
int ret = 0;
switch (new_state) {
case IOC_IDLE:
case IOC_DONE:
break;
case IOC_SNAP:
ret = -1;
break;
case IOC_COPY:
ret = -1;
break;
default:
ret = -1;
}
if (ret == 0)
else
return (ret);
}