OpenSolaris_b135/uts/sparc/os/polled_io.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * This code sets up the callbacks(vx_handlers) so that the firmware may call
 * into the kernel for console input and/or output while in the debugger.
 * The callbacks that execute in debug mode must be careful to not
 * allocate memory, access mutexes, etc. because most kernel services are
 * not available during this mode.
 *
 * This code, and the underlying code that supports the polled input, is very
 * hard to debug.  In order to get the code to execute, polled input must
 * provide input to the debugger.  If anything goes wrong with the code, then
 * it is hard to debug the debugger.  If there are any problems to debug,
 * the following is useful:
 *
 * set the polled_debug variable in /etc/system
 *	set polled_debug=1
 *
 * This variable will register the callbacks but will not throw the switch
 * in the firmware.  The callbacks can be executed by hand from the firmware.
 * Boot the system and drop down to the firmware.
 *
 *	ok " /os-io" select-dev
 *
 * The following will cause the polled_give_input to execute:
 *	ok take
 *
 * The following will cause the polled_take_input to execute:
 *	ok give
 *
 * The following will cause polled_read to execute:
 *	ok read
 */

#include <sys/stropts.h>
#include <v9/sys/prom_isa.h>
#include <sys/devops.h>
#include <sys/modctl.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/promif.h>
#include <sys/note.h>
#include <sys/consdev.h>
#include <sys/polled_io.h>
#include <sys/kdi.h>
#ifdef sun4v
#include <sys/ldoms.h>
#endif

/*
 * Internal Functions
 */
static void	polled_give_input(cell_t *cif);
static void	polled_read(cell_t *cif);
static void	polled_take_input(cell_t *cif);

static void	polled_write(cell_t *cif);
static void	polled_io_register(cons_polledio_t *,
			polled_io_console_type_t, int);
static int	polled_io_take_console(polled_io_console_type_t, int);
static int	polled_io_release_console(polled_io_console_type_t, int);

/*
 * State information regarding the input/output device
 */
static polled_device_t	polled_input_device;
static polled_device_t	polled_output_device;
static int polled_vx_handlers_init = 0;

extern void	add_vx_handler(char *name, int flag, void (*func)(cell_t *));

/*
 * This is a useful flag for debugging the entry points.   This flag
 * allows us to exercise the entry points from the firmware without
 * switching the firmware's notion of the input device.
 */
int	polled_debug = 0;

/*
 * This routine is called to initialize polled I/O.  We insert our entry
 * points so that the firmware will call into this code
 * when the switch is thrown in polled_io_take_console().
 */
void
polled_io_init(void)
{

	/*
	 * Only do the initialization once
	 */
	if (polled_vx_handlers_init != 0)
		return;
#ifdef sun4v
	if (!domaining_enabled()) {
#endif
	/*
	 * Add the vx_handlers for the different functions that
	 * need to be accessed from firmware.
	 */
	add_vx_handler("enter-input", 1, polled_give_input);

	add_vx_handler("read", 1, polled_read);

	add_vx_handler("exit-input", 1, polled_take_input);

	add_vx_handler("write", 1, polled_write);
#ifdef sun4v
	}
#endif

	/*
	 * Initialize lock to protect multiple thread access to the
	 * polled_input_device structure.  This does not protect
	 * us from access in debug mode.
	 */
	mutex_init(&polled_input_device.polled_device_lock,
	    NULL, MUTEX_DRIVER, NULL);

	/*
	 * Initialize lock to protect multiple thread access to the
	 * polled_output_device structure.  This does not protect
	 * us from access in debug mode.
	 */
	mutex_init(&polled_output_device.polled_device_lock,
	    NULL, MUTEX_DRIVER, NULL);

	polled_vx_handlers_init = 1;
}

/*
 * Register a device for input or output.  The polled_io structure
 * will be filled in with the callbacks that are appropriate for
 * that device.
 */
int
polled_io_register_callbacks(
cons_polledio_t			*polled_io,
int				flags
)
{
	/*
	 * If the input structure entries aren't filled in, then register this
	 * structure as an input device.
	 */
	if ((polled_io->cons_polledio_getchar != NULL) &&
	    (polled_io->cons_polledio_ischar != NULL)) {

		polled_io_register(polled_io, POLLED_IO_CONSOLE_INPUT, flags);
	}

	/*
	 * If the output structure entries aren't filled in, then register this
	 * structure as an output device.
	 */
	if (polled_io->cons_polledio_putchar != NULL) {

		polled_io_register(polled_io, POLLED_IO_CONSOLE_OUTPUT, flags);
	}

	cons_polledio = polled_io;

	return (DDI_SUCCESS);
}

/*
 * Sends string through the polled output interfaces when the
 * system is panicing.
 */
void
polled_io_cons_write(uchar_t *text, size_t len)
{
	cons_polledio_t *pio = polled_output_device.polled_io;
	int i;

	for (i = 0; i < len; i++)
		pio->cons_polledio_putchar(
		    pio->cons_polledio_argument, text[i]);
}

/*
 * Generic internal routine for registering a polled input or output device.
 */
/* ARGSUSED */
static void
polled_io_register(
cons_polledio_t			*polled_io,
polled_io_console_type_t	type,
int				flags
)
{
	switch (type) {
	case POLLED_IO_CONSOLE_INPUT:
		/*
		 * Grab the device lock, because we are going to access
		 * protected structure entries.  We do this before the
		 * POLLED_IO_CONSOLE_OPEN_INPUT so that we serialize
		 * registration.
		 */
		mutex_enter(&polled_input_device.polled_device_lock);

		/*
		 * Save the polled_io pointers so that we can access
		 * them later.
		 */
		polled_input_device.polled_io = polled_io;

		mutex_exit(&polled_input_device.polled_device_lock);


		if (!polled_debug) {
			/*
			 * Tell the generic console framework to
			 * repoint firmware's stdin to this keyboard device.
			 */
			(void) polled_io_take_console(type, 0);
		}

		break;

	case POLLED_IO_CONSOLE_OUTPUT:
		/*
		 * Grab the device lock, because we are going to access
		 * protected structure entries. We do this before the
		 * POLLED_IO_CONSOLE_OPEN_OUTPUT so that we serialize
		 * registration.
		 */
		mutex_enter(&polled_output_device.polled_device_lock);

		/*
		 * Save the polled_io pointers so that we can access
		 * them later.
		 */
		polled_output_device.polled_io = polled_io;

		mutex_exit(&polled_output_device.polled_device_lock);

		if (!polled_debug) {
			/*
			 * Tell the generic console framework to
			 * repoint firmware's stdout to the framebuffer.
			 */
			(void) polled_io_take_console(type, 0);
		}

		break;
	}
}

/*
 * This is the routine that is called to throw the switch from the
 * firmware's ownership of stdout/stdin to the kernel.
 */
/* ARGSUSED */
static int
polled_io_take_console(
polled_io_console_type_t	type,
int				flags
)
{

#ifdef sun4v
	if (domaining_enabled())
		return (DDI_SUCCESS);
#endif

	switch (type) {
	case POLLED_IO_CONSOLE_INPUT:
		/*
		 * Call into firmware to switch to the kernel I/O handling.
		 * We will save the old value of stdin so that we can
		 * restore it if the device is released.
		 */
#ifdef DEBUG_OBP
		/*
		 * This code is useful to trace through
		 * what the prom is doing
		 */
		prom_interpret(
		    "stdin @ swap ! trace-on \" /os-io\" input trace-off",
		    (uintptr_t)&polled_input_device.polled_old_handle,
		    0, 0, 0, 0);
#endif

		prom_interpret(
		    "stdin @ swap ! \" /os-io\" open-dev stdin !",
		    (uintptr_t)&polled_input_device.polled_old_handle,
		    0, 0, 0, 0);

		break;

	case POLLED_IO_CONSOLE_OUTPUT:
		/*
		 * Call into firmware to switch to the kernel I/O handling.
		 * We will save the old value of stdout so that we can
		 * restore it if the device is released.
		 */
		prom_interpret("stdout @ swap ! \" /os-io\" open-dev stdout !",
		    (uintptr_t)&polled_output_device.polled_old_handle,
		    0, 0, 0, 0);

		break;
	}

	return (DDI_SUCCESS);
}

/*
 * This is the routine that the firmware calls to save any state information
 * before using the input device.  This routine, and all of the
 * routines that it calls, are responsible for saving any state
 * information so that it can be restored when debug mode is over.
 *
 * WARNING: This routine runs in debug mode.
 */
static void
polled_give_input(cell_t *cif)
{
	cons_polledio_t		*polled_io;
	uint_t			out_args;

	/*
	 * Calculate the offset of the return arguments
	 */
	out_args = CIF_MIN_SIZE + p1275_cell2uint(cif[CIF_NUMBER_IN_ARGS]);

	/*
	 * There is one argument being passed back to firmware.
	 */
	cif[CIF_NUMBER_OUT_ARGS] = p1275_uint2cell((uint_t)1);
	cif[out_args] = p1275_uint2cell(CIF_SUCCESS);

	/*
	 * We check to see if there is an
	 * input device that has been registered.
	 */
	polled_io = polled_input_device.polled_io;

	if (polled_io == NULL) {
		return;
	}

	/*
	 * Call down to the lower layers to save the state.
	 */
	polled_io->cons_polledio_enter(polled_io->cons_polledio_argument);
}

/*
 * This is the routine that the firmware calls
 * when it wants to read a character.
 * We will call to the lower layers to see if there is any input data
 * available.
 *
 * WARNING: This routine runs in debug mode.
 */
static void
polled_read(cell_t *cif)
{
	uint_t				actual;
	cons_polledio_t			*polled_io;
	uint_t				in_args;
	uint_t				out_args;
	uchar_t				*buffer;
	uint_t				buflen;
	uchar_t				key;

	/*
	 * The number of arguments passed in by the firmware
	 */
	in_args = p1275_cell2uint(cif[CIF_NUMBER_IN_ARGS]);

	/*
	 * Calculate the location of the first out arg.  This location is
	 * CIF_MIN_SIZE plus the in argument locations.
	 */
	out_args = CIF_MIN_SIZE + in_args;

	/*
	 * The firmware should pass in a pointer to a buffer, and the
	 * number of characters it expects or expects to write.
	 * If 2 arguments are not passed in, then return an error.
	 */
	if (in_args != 2) {

		/*
		 * Tell firmware how many arguments we are passing back.
		 */
		cif[CIF_NUMBER_OUT_ARGS] = p1275_uint2cell((uint_t)1);

		/*
		 * Tell the firmware that we cannot give it any characters.
		 */
		cif[out_args] = p1275_uint2cell(CIF_FAILURE);

		return;
	}

	/*
	 * Get the address of where to copy the characters into.
	 */
	buffer = (uchar_t *)(uintptr_t)p1275_cell2uint(cif[CIF_MIN_SIZE+0]);

	/*
	 * Get the length of the buffer that we can copy characters into.
	 */
	buflen = p1275_cell2uint(cif[CIF_MIN_SIZE+1]);

	/*
	 * Make sure there is enough room in the buffer to copy the
	 * characters into.
	 */
	if (buflen == 0) {

		/*
		 * Tell the OBP that we cannot give it any characters.
		 */
		cif[CIF_NUMBER_OUT_ARGS] = p1275_uint2cell((uint_t)1);

		/*
		 * Tell the firmware that we cannot give it any characters.
		 */
		cif[out_args] = p1275_uint2cell(CIF_FAILURE);

		return;
	}

	/*
	 * Pass back whether or not the operation was a success or
	 * failure plus the actual number of bytes in the buffer.
	 * Tell firmware how many arguments we are passing back.
	 */
	cif[CIF_NUMBER_OUT_ARGS] = p1275_uint2cell((uint_t)2);

	/*
	 * Initialize the cif to be "no characters"
	 */
	cif[out_args+0] = p1275_uint2cell(CIF_SUCCESS);
	cif[out_args+1] = p1275_uint2cell(CIF_NO_CHARACTERS);

	/*
	 * We check to see if there is an
	 * input device that has been registered.
	 */
	polled_io = polled_input_device.polled_io;

	if (polled_io == NULL ||
	    polled_io->cons_polledio_ischar == NULL) {

		/*
		 * The cif structure is already set up to return
		 * no characters.
		 */

		return;
	}

	actual = 0;

	/*
	 * Obtain the characters
	 */
	while (polled_io->cons_polledio_ischar(
	    polled_io->cons_polledio_argument) == B_TRUE) {

		/*
		 * Make sure that we don't overrun the buffer.
		 */
		if (actual == buflen) {

			break;
		}

		/*
		 * Call down to the device to copy the input data into the
		 * buffer.
		 */
		key = polled_io->cons_polledio_getchar(
		    polled_io->cons_polledio_argument);

		*(buffer + actual) = key;

		actual++;
	}

	/*
	 * There is a special return code when there is no data.
	 */
	if (actual == 0) {

		/*
		 * The cif structure is already set up to return
		 * no characters.
		 */

		return;
	}

	/*
	 * Tell firmware how many characters we are sending it.
	 */
	cif[out_args+0] = p1275_uint2cell((uint_t)CIF_SUCCESS);
	cif[out_args+1] = p1275_uint2cell((uint_t)actual);
}

/*
 * This is the routine that firmware calls when it is giving up control of the
 * input device.  This routine, and the lower layer routines that it calls,
 * are responsible for restoring the controller state to the state it was
 * in before firmware took control.
 *
 * WARNING: This routine runs in debug mode.
 */
static void
polled_take_input(cell_t *cif)
{
	cons_polledio_t		*polled_io;
	uint_t			out_args;

	/*
	 * Calculate the offset of the return arguments
	 */
	out_args = CIF_MIN_SIZE + p1275_cell2uint(cif[CIF_NUMBER_IN_ARGS]);

	/*
	 * There is one argument being passed back to firmware.
	 */
	cif[CIF_NUMBER_OUT_ARGS] = p1275_uint2cell((uint_t)1);
	cif[out_args] = p1275_uint2cell(CIF_SUCCESS);

	/*
	 * We check the pointer to see if there is an
	 * input device that has been registered.
	 */
	polled_io = polled_input_device.polled_io;

	if (polled_io == NULL) {
		return;
	}

	/*
	 * Call down to the lower layers to save the state.
	 */
	polled_io->cons_polledio_exit(polled_io->cons_polledio_argument);
}

/*
 * This is the routine that the firmware calls when
 * it wants to write a character.
 *
 * WARNING: This routine runs in debug mode.
 */
static void
polled_write(cell_t *cif)
{
	cons_polledio_t			*polled_io;
	uint_t				in_args;
	uint_t				out_args;
	uchar_t				*buffer;
	uint_t				buflen;

	/*
	 * The number of arguments passed in by the firmware
	 */
	in_args = p1275_cell2uint(cif[CIF_NUMBER_IN_ARGS]);

	/*
	 * Calculate the location of the first out arg.  This location is
	 * CIF_MIN_SIZE (name + no. in args + no. out args) plus the
	 * in argument locations.
	 */
	out_args = CIF_MIN_SIZE + in_args;

	/*
	 * The firmware should pass in a pointer to a buffer, and the
	 * number of characters it expects or expects to write.
	 * If 2 arguments are not passed in, then return an error.
	 */
	if (in_args != 2) {

		/*
		 * Tell firmware how many arguments we are passing back.
		 */
		cif[CIF_NUMBER_OUT_ARGS] = p1275_uint2cell((uint_t)1);


		/*
		 * Tell the firmware that we cannot give it any characters.
		 */
		cif[out_args] = p1275_uint2cell(CIF_FAILURE);

		return;
	}

	/*
	 * Get the address of where to copy the characters into.
	 */
	buffer = (uchar_t *)(uintptr_t)p1275_cell2uint(cif[CIF_MIN_SIZE+0]);

	/*
	 * Get the length of the buffer that we can copy characters into.
	 */
	buflen = p1275_cell2uint(cif[CIF_MIN_SIZE+1]);

	/*
	 * Make sure there is enough room in the buffer to copy the
	 * characters into.
	 */
	if (buflen == 0) {

		/*
		 * Tell the OBP that we cannot give it any characters.
		 */
		cif[CIF_NUMBER_OUT_ARGS] = p1275_uint2cell((uint_t)1);

		/*
		 * Tell the firmware that we cannot give it any characters.
		 */
		cif[out_args] = p1275_uint2cell(CIF_FAILURE);

		return;
	}


	/*
	 * Tell the firmware how many arguments we are passing back.
	 */
	cif[CIF_NUMBER_OUT_ARGS] = p1275_uint2cell((uint_t)2);

	/*
	 * Initialize the cif to success
	 */
	cif[out_args+0] = p1275_uint2cell(CIF_SUCCESS);
	cif[out_args+1] = p1275_uint2cell(0);

	/*
	 * We check the pointer to see if there is an
	 * input device that has been registered.
	 */
	polled_io = polled_output_device.polled_io;

	if (polled_io == NULL) {

		/*
		 * The cif is already initialized
		 */
		return;
	}

	polled_io_cons_write(buffer, (size_t)buflen);

	/*
	 * Tell the firmware how many characters we are sending it.
	 */
	cif[out_args+0] = p1275_uint2cell((uint_t)CIF_SUCCESS);
	cif[out_args+1] = p1275_uint2cell((uint_t)buflen);
}