2007-07-18 05:37:06 +04:00
/*
* xen console driver interface to hvc_console . c
*
* ( c ) 2007 Gerd Hoffmann < kraxel @ suse . de >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/console.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/init.h>
# include <linux/types.h>
# include <asm/xen/hypervisor.h>
# include <xen/page.h>
# include <xen/events.h>
# include <xen/interface/io/console.h>
# include <xen/hvc-console.h>
# include "hvc_console.h"
# define HVC_COOKIE 0x58656e /* "Xen" in hex */
static struct hvc_struct * hvc ;
static int xencons_irq ;
/* ------------------------------------------------------------------ */
2008-05-27 02:31:25 +04:00
static unsigned long console_pfn = ~ 0ul ;
2007-07-18 05:37:06 +04:00
static inline struct xencons_interface * xencons_interface ( void )
{
2008-05-27 02:31:25 +04:00
if ( console_pfn = = ~ 0ul )
return mfn_to_virt ( xen_start_info - > console . domU . mfn ) ;
else
return __va ( console_pfn < < PAGE_SHIFT ) ;
2007-07-18 05:37:06 +04:00
}
static inline void notify_daemon ( void )
{
/* Use evtchn: this is called early, before irq is set up. */
notify_remote_via_evtchn ( xen_start_info - > console . domU . evtchn ) ;
}
static int write_console ( uint32_t vtermno , const char * data , int len )
{
struct xencons_interface * intf = xencons_interface ( ) ;
XENCONS_RING_IDX cons , prod ;
int sent = 0 ;
cons = intf - > out_cons ;
prod = intf - > out_prod ;
mb ( ) ; /* update queue values before going on */
BUG_ON ( ( prod - cons ) > sizeof ( intf - > out ) ) ;
while ( ( sent < len ) & & ( ( prod - cons ) < sizeof ( intf - > out ) ) )
intf - > out [ MASK_XENCONS_IDX ( prod + + , intf - > out ) ] = data [ sent + + ] ;
wmb ( ) ; /* write ring before updating pointer */
intf - > out_prod = prod ;
notify_daemon ( ) ;
return sent ;
}
static int read_console ( uint32_t vtermno , char * buf , int len )
{
struct xencons_interface * intf = xencons_interface ( ) ;
XENCONS_RING_IDX cons , prod ;
int recv = 0 ;
cons = intf - > in_cons ;
prod = intf - > in_prod ;
mb ( ) ; /* get pointers before reading ring */
BUG_ON ( ( prod - cons ) > sizeof ( intf - > in ) ) ;
while ( cons ! = prod & & recv < len )
buf [ recv + + ] = intf - > in [ MASK_XENCONS_IDX ( cons + + , intf - > in ) ] ;
mb ( ) ; /* read ring before consuming */
intf - > in_cons = cons ;
notify_daemon ( ) ;
return recv ;
}
static struct hv_ops hvc_ops = {
. get_chars = read_console ,
. put_chars = write_console ,
2008-06-20 17:24:08 +04:00
. notifier_add = notifier_add_irq ,
. notifier_del = notifier_del_irq ,
2008-10-14 03:12:48 +04:00
. notifier_hangup = notifier_hangup_irq ,
2007-07-18 05:37:06 +04:00
} ;
static int __init xen_init ( void )
{
struct hvc_struct * hp ;
2008-08-20 00:16:17 +04:00
if ( ! xen_pv_domain ( ) | |
xen_initial_domain ( ) | |
2008-05-27 02:31:25 +04:00
! xen_start_info - > console . domU . evtchn )
return - ENODEV ;
2007-07-18 05:37:06 +04:00
xencons_irq = bind_evtchn_to_irq ( xen_start_info - > console . domU . evtchn ) ;
if ( xencons_irq < 0 )
2008-05-27 02:31:25 +04:00
xencons_irq = 0 ; /* NO_IRQ */
2007-07-18 05:37:06 +04:00
hp = hvc_alloc ( HVC_COOKIE , xencons_irq , & hvc_ops , 256 ) ;
if ( IS_ERR ( hp ) )
return PTR_ERR ( hp ) ;
hvc = hp ;
2008-05-27 02:31:25 +04:00
console_pfn = mfn_to_pfn ( xen_start_info - > console . domU . mfn ) ;
2007-07-18 05:37:06 +04:00
return 0 ;
}
2008-05-27 02:31:25 +04:00
void xen_console_resume ( void )
{
if ( xencons_irq )
rebind_evtchn_irq ( xen_start_info - > console . domU . evtchn , xencons_irq ) ;
}
2007-07-18 05:37:06 +04:00
static void __exit xen_fini ( void )
{
if ( hvc )
hvc_remove ( hvc ) ;
}
static int xen_cons_init ( void )
{
2008-08-20 00:16:17 +04:00
if ( ! xen_pv_domain ( ) )
2007-07-18 05:37:06 +04:00
return 0 ;
hvc_instantiate ( HVC_COOKIE , 0 , & hvc_ops ) ;
return 0 ;
}
module_init ( xen_init ) ;
module_exit ( xen_fini ) ;
console_initcall ( xen_cons_init ) ;
2008-05-27 02:31:00 +04:00
static void raw_console_write ( const char * str , int len )
{
while ( len > 0 ) {
int rc = HYPERVISOR_console_io ( CONSOLEIO_write , len , ( char * ) str ) ;
if ( rc < = 0 )
break ;
str + = rc ;
len - = rc ;
}
}
# ifdef CONFIG_EARLY_PRINTK
2007-07-18 05:37:06 +04:00
static void xenboot_write_console ( struct console * console , const char * string ,
unsigned len )
{
unsigned int linelen , off = 0 ;
const char * pos ;
2008-05-27 02:31:00 +04:00
raw_console_write ( string , len ) ;
2008-05-27 02:31:12 +04:00
write_console ( 0 , " (early) " , 8 ) ;
2007-07-18 05:37:06 +04:00
while ( off < len & & NULL ! = ( pos = strchr ( string + off , ' \n ' ) ) ) {
linelen = pos - string + off ;
if ( off + linelen > len )
break ;
write_console ( 0 , string + off , linelen ) ;
write_console ( 0 , " \r \n " , 2 ) ;
off + = linelen + 1 ;
}
if ( off < len )
write_console ( 0 , string + off , len - off ) ;
}
struct console xenboot_console = {
. name = " xenboot " ,
. write = xenboot_write_console ,
2008-05-27 02:31:00 +04:00
. flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME ,
2007-07-18 05:37:06 +04:00
} ;
2008-05-27 02:31:00 +04:00
# endif /* CONFIG_EARLY_PRINTK */
2008-05-27 02:30:59 +04:00
void xen_raw_console_write ( const char * str )
{
2008-05-27 02:31:00 +04:00
raw_console_write ( str , strlen ( str ) ) ;
2008-05-27 02:30:59 +04:00
}
void xen_raw_printk ( const char * fmt , . . . )
{
static char buf [ 512 ] ;
va_list ap ;
va_start ( ap , fmt ) ;
vsnprintf ( buf , sizeof ( buf ) , fmt , ap ) ;
va_end ( ap ) ;
xen_raw_console_write ( buf ) ;
}