@ -32,210 +32,9 @@
# include "qemu/iov.h"
# include "qemu/bitops.h"
# include "libqos/malloc.h"
# include "libqos/malloc-pc.h"
# include "libqos/malloc-generic.h"
# include "libqos/e1000e.h"
# define E1000E_IMS (0x00d0)
# define E1000E_STATUS (0x0008)
# define E1000E_STATUS_LU BIT(1)
# define E1000E_STATUS_ASDV1000 BIT(9)
# define E1000E_CTRL (0x0000)
# define E1000E_CTRL_RESET BIT(26)
# define E1000E_RCTL (0x0100)
# define E1000E_RCTL_EN BIT(1)
# define E1000E_RCTL_UPE BIT(3)
# define E1000E_RCTL_MPE BIT(4)
# define E1000E_RFCTL (0x5008)
# define E1000E_RFCTL_EXTEN BIT(15)
# define E1000E_TCTL (0x0400)
# define E1000E_TCTL_EN BIT(1)
# define E1000E_CTRL_EXT (0x0018)
# define E1000E_CTRL_EXT_DRV_LOAD BIT(28)
# define E1000E_CTRL_EXT_TXLSFLOW BIT(22)
# define E1000E_RX0_MSG_ID (0)
# define E1000E_TX0_MSG_ID (1)
# define E1000E_OTHER_MSG_ID (2)
# define E1000E_IVAR (0x00E4)
# define E1000E_IVAR_TEST_CFG ((E1000E_RX0_MSG_ID << 0) | BIT(3) | \
( E1000E_TX0_MSG_ID < < 8 ) | BIT ( 11 ) | \
( E1000E_OTHER_MSG_ID < < 16 ) | BIT ( 19 ) | \
BIT ( 31 ) )
# define E1000E_RING_LEN (0x1000)
# define E1000E_TXD_LEN (16)
# define E1000E_RXD_LEN (16)
# define E1000E_TDBAL (0x3800)
# define E1000E_TDBAH (0x3804)
# define E1000E_TDLEN (0x3808)
# define E1000E_TDH (0x3810)
# define E1000E_TDT (0x3818)
# define E1000E_RDBAL (0x2800)
# define E1000E_RDBAH (0x2804)
# define E1000E_RDLEN (0x2808)
# define E1000E_RDH (0x2810)
# define E1000E_RDT (0x2818)
typedef struct e1000e_device {
QPCIDevice * pci_dev ;
QPCIBar mac_regs ;
uint64_t tx_ring ;
uint64_t rx_ring ;
} e1000e_device ;
static int test_sockets [ 2 ] ;
static QGuestAllocator test_alloc ;
static QPCIBus * test_bus ;
static void e1000e_pci_foreach_callback ( QPCIDevice * dev , int devfn , void * data )
{
QPCIDevice * * res = data ;
g_assert_null ( * res ) ;
* res = dev ;
}
static QPCIDevice * e1000e_device_find ( QPCIBus * bus )
{
static const int e1000e_vendor_id = 0x8086 ;
static const int e1000e_dev_id = 0x10D3 ;
QPCIDevice * e1000e_dev = NULL ;
qpci_device_foreach ( bus , e1000e_vendor_id , e1000e_dev_id ,
e1000e_pci_foreach_callback , & e1000e_dev ) ;
g_assert_nonnull ( e1000e_dev ) ;
return e1000e_dev ;
}
static void e1000e_macreg_write ( e1000e_device * d , uint32_t reg , uint32_t val )
{
qpci_io_writel ( d - > pci_dev , d - > mac_regs , reg , val ) ;
}
static uint32_t e1000e_macreg_read ( e1000e_device * d , uint32_t reg )
{
return qpci_io_readl ( d - > pci_dev , d - > mac_regs , reg ) ;
}
static void e1000e_device_init ( QPCIBus * bus , e1000e_device * d )
{
uint32_t val ;
d - > pci_dev = e1000e_device_find ( bus ) ;
/* Enable the device */
qpci_device_enable ( d - > pci_dev ) ;
/* Map BAR0 (mac registers) */
d - > mac_regs = qpci_iomap ( d - > pci_dev , 0 , NULL ) ;
/* Reset the device */
val = e1000e_macreg_read ( d , E1000E_CTRL ) ;
e1000e_macreg_write ( d , E1000E_CTRL , val | E1000E_CTRL_RESET ) ;
/* Enable and configure MSI-X */
qpci_msix_enable ( d - > pci_dev ) ;
e1000e_macreg_write ( d , E1000E_IVAR , E1000E_IVAR_TEST_CFG ) ;
/* Check the device status - link and speed */
val = e1000e_macreg_read ( d , E1000E_STATUS ) ;
g_assert_cmphex ( val & ( E1000E_STATUS_LU | E1000E_STATUS_ASDV1000 ) ,
= = , E1000E_STATUS_LU | E1000E_STATUS_ASDV1000 ) ;
/* Initialize TX/RX logic */
e1000e_macreg_write ( d , E1000E_RCTL , 0 ) ;
e1000e_macreg_write ( d , E1000E_TCTL , 0 ) ;
/* Notify the device that the driver is ready */
val = e1000e_macreg_read ( d , E1000E_CTRL_EXT ) ;
e1000e_macreg_write ( d , E1000E_CTRL_EXT ,
val | E1000E_CTRL_EXT_DRV_LOAD | E1000E_CTRL_EXT_TXLSFLOW ) ;
/* Allocate and setup TX ring */
d - > tx_ring = guest_alloc ( & test_alloc , E1000E_RING_LEN ) ;
g_assert ( d - > tx_ring ! = 0 ) ;
e1000e_macreg_write ( d , E1000E_TDBAL , ( uint32_t ) d - > tx_ring ) ;
e1000e_macreg_write ( d , E1000E_TDBAH , ( uint32_t ) ( d - > tx_ring > > 32 ) ) ;
e1000e_macreg_write ( d , E1000E_TDLEN , E1000E_RING_LEN ) ;
e1000e_macreg_write ( d , E1000E_TDT , 0 ) ;
e1000e_macreg_write ( d , E1000E_TDH , 0 ) ;
/* Enable transmit */
e1000e_macreg_write ( d , E1000E_TCTL , E1000E_TCTL_EN ) ;
/* Allocate and setup RX ring */
d - > rx_ring = guest_alloc ( & test_alloc , E1000E_RING_LEN ) ;
g_assert ( d - > rx_ring ! = 0 ) ;
e1000e_macreg_write ( d , E1000E_RDBAL , ( uint32_t ) d - > rx_ring ) ;
e1000e_macreg_write ( d , E1000E_RDBAH , ( uint32_t ) ( d - > rx_ring > > 32 ) ) ;
e1000e_macreg_write ( d , E1000E_RDLEN , E1000E_RING_LEN ) ;
e1000e_macreg_write ( d , E1000E_RDT , 0 ) ;
e1000e_macreg_write ( d , E1000E_RDH , 0 ) ;
/* Enable receive */
e1000e_macreg_write ( d , E1000E_RFCTL , E1000E_RFCTL_EXTEN ) ;
e1000e_macreg_write ( d , E1000E_RCTL , E1000E_RCTL_EN |
E1000E_RCTL_UPE |
E1000E_RCTL_MPE ) ;
/* Enable all interrupts */
e1000e_macreg_write ( d , E1000E_IMS , 0xFFFFFFFF ) ;
}
static void e1000e_tx_ring_push ( e1000e_device * d , void * descr )
{
uint32_t tail = e1000e_macreg_read ( d , E1000E_TDT ) ;
uint32_t len = e1000e_macreg_read ( d , E1000E_TDLEN ) / E1000E_TXD_LEN ;
memwrite ( d - > tx_ring + tail * E1000E_TXD_LEN , descr , E1000E_TXD_LEN ) ;
e1000e_macreg_write ( d , E1000E_TDT , ( tail + 1 ) % len ) ;
/* Read WB data for the packet transmitted */
memread ( d - > tx_ring + tail * E1000E_TXD_LEN , descr , E1000E_TXD_LEN ) ;
}
static void e1000e_rx_ring_push ( e1000e_device * d , void * descr )
{
uint32_t tail = e1000e_macreg_read ( d , E1000E_RDT ) ;
uint32_t len = e1000e_macreg_read ( d , E1000E_RDLEN ) / E1000E_RXD_LEN ;
memwrite ( d - > rx_ring + tail * E1000E_RXD_LEN , descr , E1000E_RXD_LEN ) ;
e1000e_macreg_write ( d , E1000E_RDT , ( tail + 1 ) % len ) ;
/* Read WB data for the packet received */
memread ( d - > rx_ring + tail * E1000E_RXD_LEN , descr , E1000E_RXD_LEN ) ;
}
static void e1000e_wait_isr ( e1000e_device * d , uint16_t msg_id )
{
guint64 end_time = g_get_monotonic_time ( ) + 5 * G_TIME_SPAN_SECOND ;
do {
if ( qpci_msix_pending ( d - > pci_dev , msg_id ) ) {
return ;
}
clock_step ( 10000 ) ;
} while ( g_get_monotonic_time ( ) < end_time ) ;
g_error ( " Timeout expired " ) ;
}
static void e1000e_send_verify ( e1000e_device * d )
static void e1000e_send_verify ( QE1000E * d , int * test_sockets , QGuestAllocator * alloc )
{
struct {
uint64_t buffer_addr ;
@ -268,7 +67,7 @@ static void e1000e_send_verify(e1000e_device *d)
uint32_t recv_len ;
/* Prepare test data buffer */
uint64_t data = guest_alloc ( & test_ alloc, data_len ) ;
uint64_t data = guest_alloc ( alloc , data_len ) ;
memwrite ( data , " TEST " , 5 ) ;
/* Prepare TX descriptor */
@ -296,10 +95,10 @@ static void e1000e_send_verify(e1000e_device *d)
g_assert_cmpstr ( buffer , = = , " TEST " ) ;
/* Free test data buffer */
guest_free ( & test_ alloc, data ) ;
guest_free ( alloc , data ) ;
}
static void e1000e_receive_verify ( e1000e_device * d )
static void e1000e_receive_verify ( QE1000E * d , int * test_sockets , QGuestAllocator * alloc )
{
union {
struct {
@ -348,7 +147,7 @@ static void e1000e_receive_verify(e1000e_device *d)
g_assert_cmpint ( ret , = = , sizeof ( test ) + sizeof ( len ) ) ;
/* Prepare test data buffer */
uint64_t data = guest_alloc ( & test_ alloc, data_len ) ;
uint64_t data = guest_alloc ( alloc , data_len ) ;
/* Prepare RX descriptor */
memset ( & descr , 0 , sizeof ( descr ) ) ;
@ -369,109 +168,108 @@ static void e1000e_receive_verify(e1000e_device *d)
g_assert_cmpstr ( buffer , = = , " TEST " ) ;
/* Free test data buffer */
guest_free ( & test_alloc , data ) ;
}
static void e1000e_device_clear ( QPCIBus * bus , e1000e_device * d )
{
qpci_iounmap ( d - > pci_dev , d - > mac_regs ) ;
qpci_msix_disable ( d - > pci_dev ) ;
guest_free ( alloc , data ) ;
}
static void data_test_init ( e1000e_device * d )
static void test_e1000e_init ( void * obj , void * data , QGuestAllocator * alloc )
{
char * cmdline ;
int ret = socketpair ( PF_UNIX , SOCK_STREAM , 0 , test_sockets ) ;
g_assert_cmpint ( ret , ! = , - 1 ) ;
cmdline = g_strdup_printf ( " -netdev socket,fd=%d,id=hs0 "
" -device e1000e,netdev=hs0 " , test_sockets [ 1 ] ) ;
g_assert_nonnull ( cmdline ) ;
qtest_start ( cmdline ) ;
g_free ( cmdline ) ;
pc_alloc_init ( & test_alloc , global_qtest , 0 ) ;
test_bus = qpci_new_pc ( global_qtest , & test_alloc ) ;
g_assert_nonnull ( test_bus ) ;
e1000e_device_init ( test_bus , d ) ;
/* init does nothing */
}
static void data_test_clear ( e1000e_device * d )
static void test_e1000e_tx ( void * obj , void * data , QGuestAllocator * alloc )
{
e1000e_device_clear ( test_bus , d ) ;
close ( test_sockets [ 0 ] ) ;
alloc_destroy ( & test_alloc ) ;
g_free ( d - > pci_dev ) ;
qpci_free_pc ( test_bus ) ;
qtest_end ( ) ;
}
static void test_e1000e_init ( gconstpointer data )
{
e1000e_device d ;
data_test_init ( & d ) ;
data_test_clear ( & d ) ;
}
static void test_e1000e_tx ( gconstpointer data )
{
e1000e_device d ;
QE1000E_PCI * e1000e = obj ;
QE1000E * d = & e1000e - > e1000e ;
QOSGraphObject * e_object = obj ;
QPCIDevice * dev = e_object - > get_driver ( e_object , " pci-device " ) ;
/* FIXME: add spapr support */
if ( qpci_check_buggy_msi ( dev ) ) {
return ;
}
data_test_init ( & d ) ;
e1000e_send_verify ( & d ) ;
data_test_clear ( & d ) ;
e1000e_send_verify ( d , data , alloc ) ;
}
static void test_e1000e_rx ( gconstpointer data )
static void test_e1000e_rx ( void * obj , void * data , QGuestAllocator * alloc )
{
e1000e_device d ;
QE1000E_PCI * e1000e = obj ;
QE1000E * d = & e1000e - > e1000e ;
QOSGraphObject * e_object = obj ;
QPCIDevice * dev = e_object - > get_driver ( e_object , " pci-device " ) ;
/* FIXME: add spapr support */
if ( qpci_check_buggy_msi ( dev ) ) {
return ;
}
data_test_init ( & d ) ;
e1000e_receive_verify ( & d ) ;
data_test_clear ( & d ) ;
e1000e_receive_verify ( d , data , alloc ) ;
}
static void test_e1000e_multiple_transfers ( gconstpointer data )
static void test_e1000e_multiple_transfers ( void * obj , void * data ,
QGuestAllocator * alloc )
{
static const long iterations = 4 * 1024 ;
long i ;
e1000e_device d ;
QE1000E_PCI * e1000e = obj ;
QE1000E * d = & e1000e - > e1000e ;
QOSGraphObject * e_object = obj ;
QPCIDevice * dev = e_object - > get_driver ( e_object , " pci-device " ) ;
data_test_init ( & d ) ;
/* FIXME: add spapr support */
if ( qpci_check_buggy_msi ( dev ) ) {
return ;
}
for ( i = 0 ; i < iterations ; i + + ) {
e1000e_send_verify ( & d ) ;
e1000e_receive_verify ( & d ) ;
e1000e_send_verify ( d , data , alloc ) ;
e1000e_receive_verify ( d , data , alloc ) ;
}
data_test_clear ( & d ) ;
}
static void test_e1000e_hotplug ( gconstpointer data )
static void test_e1000e_hotplug ( void * obj , void * data , QGuestAllocator * alloc )
{
qtest_start ( " -device e1000e " ) ;
qtest_qmp_device_add ( " e1000e " , " e1000e_net " , " {'addr': '0x06'} " ) ;
qpci_unplug_acpi_device_test ( " e1000e_net " , 0x06 ) ;
}
static void data_test_clear ( void * sockets )
{
int * test_sockets = sockets ;
qtest_end ( ) ;
close ( test_sockets [ 0 ] ) ;
qos_invalidate_command_line ( ) ;
close ( test_sockets [ 1 ] ) ;
g_free ( test_sockets ) ;
}
int main ( int argc , char * * argv )
static void * data_test_init ( GString * cmd_line , void * arg )
{
g_test_init ( & argc , & argv , NULL ) ;
int * test_sockets = g_new ( int , 2 ) ;
int ret = socketpair ( PF_UNIX , SOCK_STREAM , 0 , test_sockets ) ;
g_assert_cmpint ( ret , ! = , - 1 ) ;
qtest_add_data_func ( " e1000e/init " , NULL , test_e1000e_init ) ;
qtest_add_data_func ( " e1000e/tx " , NULL , test_e1000e_tx ) ;
qtest_add_data_func ( " e1000e/rx " , NULL , test_e1000e_rx ) ;
qtest_add_data_func ( " e1000e/multiple_transfers " , NULL ,
test_e1000e_multiple_transfers ) ;
qtest_add_data_func ( " e1000e/hotplug " , NULL , test_e1000e_hotplug ) ;
g_string_append_printf ( cmd_line , " -netdev socket,fd=%d,id=hs0 " ,
test_sockets [ 1 ] ) ;
return g_test_run ( ) ;
g_test_queue_destroy ( data_test_clear , test_sockets ) ;
return test_sockets ;
}
static void register_e1000e_test ( void )
{
QOSGraphTestOptions opts = {
. before = data_test_init ,
} ;
qos_add_test ( " init " , " e1000e " , test_e1000e_init , & opts ) ;
qos_add_test ( " tx " , " e1000e " , test_e1000e_tx , & opts ) ;
qos_add_test ( " rx " , " e1000e " , test_e1000e_rx , & opts ) ;
qos_add_test ( " multiple_transfers " , " e1000e " ,
test_e1000e_multiple_transfers , & opts ) ;
qos_add_test ( " hotplug " , " e1000e " , test_e1000e_hotplug , & opts ) ;
}
libqos_init ( register_e1000e_test ) ;