@ -27,6 +27,8 @@
# include "qemu/error-report.h"
# include "qemu/module.h"
# include "qemu/cutils.h"
# include "qapi/qmp/qerror.h"
# include "qom/object_interfaces.h"
# include CONFIG_DEVICES
# ifdef CONFIG_PSERIES
# include "hw/ppc/spapr_rtas.h"
@ -34,11 +36,25 @@
# define MAX_IRQ 256
# define TYPE_QTEST "qtest"
OBJECT_DECLARE_SIMPLE_TYPE ( QTest , QTEST )
struct QTest {
Object parent ;
bool has_machine_link ;
char * chr_name ;
Chardev * chr ;
CharBackend qtest_chr ;
char * log ;
} ;
bool qtest_allowed ;
static DeviceState * irq_intercept_dev ;
static FILE * qtest_log_fp ;
static CharBackend qtest_chr ;
static QTest * qtest ;
static GString * inbuf ;
static int irq_levels [ MAX_IRQ ] ;
static qemu_timeval start_time ;
@ -320,7 +336,7 @@ static void qtest_irq_handler(void *opaque, int n, int level)
qemu_set_irq ( old_irq , level ) ;
if ( irq_levels [ n ] ! = level ) {
CharBackend * chr = & qtest_chr ;
CharBackend * chr = & qtest - > qtest _chr;
irq_levels [ n ] = level ;
qtest_send_prefix ( chr ) ;
qtest_sendf ( chr , " IRQ %s %d \n " ,
@ -849,18 +865,39 @@ static void qtest_event(void *opaque, QEMUChrEvent event)
break ;
}
}
void qtest_server_init ( const char * qtest_chrdev , const char * qtest_log , Error * * errp )
{
ERRP_GUARD ( ) ;
Chardev * chr ;
Object * qtest ;
chr = qemu_chr_new ( " qtest " , qtest_chrdev , NULL ) ;
if ( chr = = NULL ) {
error_setg ( errp , " Failed to initialize device for qtest: \" %s \" " ,
qtest_chrdev ) ;
return ;
}
qtest = object_new ( TYPE_QTEST ) ;
object_property_set_str ( qtest , " chardev " , " qtest " , & error_abort ) ;
if ( qtest_log ) {
object_property_set_str ( qtest , " log " , qtest_log , & error_abort ) ;
}
object_property_add_child ( qdev_get_machine ( ) , " qtest " , qtest ) ;
user_creatable_complete ( USER_CREATABLE ( qtest ) , errp ) ;
if ( * errp ) {
object_unparent ( qtest ) ;
}
object_unref ( OBJECT ( chr ) ) ;
object_unref ( qtest ) ;
}
static bool qtest_server_start ( QTest * q , Error * * errp )
{
Chardev * chr = q - > chr ;
const char * qtest_log = q - > log ;
if ( qtest_log ) {
if ( strcmp ( qtest_log , " none " ) ! = 0 ) {
qtest_log_fp = fopen ( qtest_log , " w+ " ) ;
@ -869,16 +906,20 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error **
qtest_log_fp = stderr ;
}
qemu_chr_fe_init ( & qtest_chr , chr , errp ) ;
qemu_chr_fe_set_handlers ( & qtest_chr , qtest_can_read , qtest_read ,
qtest_event , NULL , & qtest_chr , NULL , true ) ;
qemu_chr_fe_set_echo ( & qtest_chr , true ) ;
if ( ! qemu_chr_fe_init ( & q - > qtest_chr , chr , errp ) ) {
return false ;
}
qemu_chr_fe_set_handlers ( & q - > qtest_chr , qtest_can_read , qtest_read ,
qtest_event , NULL , & q - > qtest_chr , NULL , true ) ;
qemu_chr_fe_set_echo ( & q - > qtest_chr , true ) ;
inbuf = g_string_new ( " " ) ;
if ( ! qtest_server_send ) {
qtest_server_set_send_handler ( qtest_server_char_be_send , & qtest_chr ) ;
qtest_server_set_send_handler ( qtest_server_char_be_send , & q - > q test_chr) ;
}
qtest = q ;
return true ;
}
void qtest_server_set_send_handler ( void ( * send ) ( void * , const char * ) ,
@ -890,7 +931,7 @@ void qtest_server_set_send_handler(void (*send)(void*, const char*),
bool qtest_driver ( void )
{
return qtest_chr . chr ! = NULL ;
return qtest & & qtest - > qtest _chr . chr ! = NULL ;
}
void qtest_server_inproc_recv ( void * dummy , const char * buf )
@ -905,3 +946,129 @@ void qtest_server_inproc_recv(void *dummy, const char *buf)
g_string_truncate ( gstr , 0 ) ;
}
}
static void qtest_complete ( UserCreatable * uc , Error * * errp )
{
QTest * q = QTEST ( uc ) ;
if ( qtest ) {
error_setg ( errp , " Only one instance of qtest can be created " ) ;
return ;
}
if ( ! q - > chr_name ) {
error_setg ( errp , " No backend specified " ) ;
return ;
}
if ( OBJECT ( uc ) - > parent ! = qdev_get_machine ( ) ) {
q - > has_machine_link = true ;
object_property_add_const_link ( qdev_get_machine ( ) , " qtest " , OBJECT ( uc ) ) ;
} else {
/* -qtest was used. */
}
qtest_server_start ( q , errp ) ;
}
static void qtest_unparent ( Object * obj )
{
QTest * q = QTEST ( obj ) ;
if ( qtest = = q ) {
qemu_chr_fe_disconnect ( & q - > qtest_chr ) ;
assert ( ! qtest_opened ) ;
qemu_chr_fe_deinit ( & q - > qtest_chr , false ) ;
if ( qtest_log_fp ) {
fclose ( qtest_log_fp ) ;
qtest_log_fp = NULL ;
}
qtest = NULL ;
}
if ( q - > has_machine_link ) {
object_property_del ( qdev_get_machine ( ) , " qtest " ) ;
q - > has_machine_link = false ;
}
}
static void qtest_set_log ( Object * obj , const char * value , Error * * errp )
{
QTest * q = QTEST ( obj ) ;
if ( qtest = = q ) {
error_setg ( errp , QERR_PERMISSION_DENIED ) ;
} else {
g_free ( q - > log ) ;
q - > log = g_strdup ( value ) ;
}
}
static char * qtest_get_log ( Object * obj , Error * * errp )
{
QTest * q = QTEST ( obj ) ;
return g_strdup ( q - > log ) ;
}
static void qtest_set_chardev ( Object * obj , const char * value , Error * * errp )
{
QTest * q = QTEST ( obj ) ;
Chardev * chr ;
if ( qtest = = q ) {
error_setg ( errp , QERR_PERMISSION_DENIED ) ;
return ;
}
chr = qemu_chr_find ( value ) ;
if ( ! chr ) {
error_setg ( errp , " Cannot find character device '%s' " , value ) ;
return ;
}
g_free ( q - > chr_name ) ;
q - > chr_name = g_strdup ( value ) ;
if ( q - > chr ) {
object_unref ( q - > chr ) ;
}
q - > chr = chr ;
object_ref ( chr ) ;
}
static char * qtest_get_chardev ( Object * obj , Error * * errp )
{
QTest * q = QTEST ( obj ) ;
return g_strdup ( q - > chr_name ) ;
}
static void qtest_class_init ( ObjectClass * oc , void * data )
{
UserCreatableClass * ucc = USER_CREATABLE_CLASS ( oc ) ;
oc - > unparent = qtest_unparent ;
ucc - > complete = qtest_complete ;
object_class_property_add_str ( oc , " chardev " ,
qtest_get_chardev , qtest_set_chardev ) ;
object_class_property_add_str ( oc , " log " ,
qtest_get_log , qtest_set_log ) ;
}
static const TypeInfo qtest_info = {
. name = TYPE_QTEST ,
. parent = TYPE_OBJECT ,
. class_init = qtest_class_init ,
. instance_size = sizeof ( QTest ) ,
. interfaces = ( InterfaceInfo [ ] ) {
{ TYPE_USER_CREATABLE } ,
{ }
}
} ;
static void register_types ( void )
{
type_register_static ( & qtest_info ) ;
}
type_init ( register_types ) ;