@ -14,6 +14,8 @@
# include "qemu/osdep.h"
# include "fw_cfg.h"
# include "malloc-pc.h"
# include "libqos-malloc.h"
# include "../libqtest.h"
# include "qemu/bswap.h"
# include "hw/nvram/fw_cfg.h"
@ -60,6 +62,60 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key)
qtest_writew ( fw_cfg - > qts , fw_cfg - > base , key ) ;
}
static void qfw_cfg_dma_transfer ( QFWCFG * fw_cfg , QOSState * qs , void * address ,
uint32_t length , uint32_t control )
{
FWCfgDmaAccess access ;
uint32_t addr ;
uint64_t guest_access_addr ;
uint64_t gaddr ;
/* create a data buffer in guest memory */
gaddr = guest_alloc ( & qs - > alloc , length ) ;
if ( control & FW_CFG_DMA_CTL_WRITE ) {
qtest_bufwrite ( fw_cfg - > qts , gaddr , address , length ) ;
}
access . address = cpu_to_be64 ( gaddr ) ;
access . length = cpu_to_be32 ( length ) ;
access . control = cpu_to_be32 ( control ) ;
/* now create a separate buffer in guest memory for 'access' */
guest_access_addr = guest_alloc ( & qs - > alloc , sizeof ( access ) ) ;
qtest_bufwrite ( fw_cfg - > qts , guest_access_addr , & access , sizeof ( access ) ) ;
/* write lower 32 bits of address */
addr = cpu_to_be32 ( ( uint32_t ) ( uintptr_t ) guest_access_addr ) ;
qtest_outl ( fw_cfg - > qts , fw_cfg - > base + 8 , addr ) ;
/* write upper 32 bits of address */
addr = cpu_to_be32 ( ( uint32_t ) ( uintptr_t ) ( guest_access_addr > > 32 ) ) ;
qtest_outl ( fw_cfg - > qts , fw_cfg - > base + 4 , addr ) ;
g_assert ( ! ( be32_to_cpu ( access . control ) & FW_CFG_DMA_CTL_ERROR ) ) ;
if ( control & FW_CFG_DMA_CTL_READ ) {
qtest_bufread ( fw_cfg - > qts , gaddr , address , length ) ;
}
guest_free ( & qs - > alloc , guest_access_addr ) ;
guest_free ( & qs - > alloc , gaddr ) ;
}
static void qfw_cfg_write_entry ( QFWCFG * fw_cfg , QOSState * qs , uint16_t key ,
void * buf , uint32_t len )
{
qfw_cfg_select ( fw_cfg , key ) ;
qfw_cfg_dma_transfer ( fw_cfg , qs , buf , len , FW_CFG_DMA_CTL_WRITE ) ;
}
static void qfw_cfg_read_entry ( QFWCFG * fw_cfg , QOSState * qs , uint16_t key ,
void * buf , uint32_t len )
{
qfw_cfg_select ( fw_cfg , key ) ;
qfw_cfg_dma_transfer ( fw_cfg , qs , buf , len , FW_CFG_DMA_CTL_READ ) ;
}
static bool find_pdir_entry ( QFWCFG * fw_cfg , const char * filename ,
uint16_t * sel , uint32_t * size )
{
@ -121,6 +177,89 @@ size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename,
return filesize ;
}
/*
* The caller need check the return value . When the return value is
* nonzero , it means that some bytes have been transferred .
*
* If the fw_cfg file in question is smaller than the allocated & passed - in
* buffer , then the first len bytes were read .
*
* If the fw_cfg file in question is larger than the passed - in
* buffer , then the return value explains how much was actually read .
*
* It is illegal to call this function if fw_cfg does not support DMA
* interface . The caller should ensure that DMA is supported before
* calling this function .
*
* Passed QOSState pointer qs must be initialized . qs - > alloc must also be
* properly initialized .
*/
size_t qfw_cfg_read_file ( QFWCFG * fw_cfg , QOSState * qs , const char * filename ,
void * data , size_t buflen )
{
uint32_t len = 0 ;
uint16_t sel ;
uint32_t id ;
g_assert ( qs ) ;
g_assert ( filename ) ;
g_assert ( data ) ;
g_assert ( buflen ) ;
/* check if DMA is supported since we use DMA for read */
id = qfw_cfg_get_u32 ( fw_cfg , FW_CFG_ID ) ;
g_assert ( id & FW_CFG_VERSION_DMA ) ;
if ( find_pdir_entry ( fw_cfg , filename , & sel , & len ) ) {
if ( len > buflen ) {
len = buflen ;
}
qfw_cfg_read_entry ( fw_cfg , qs , sel , data , len ) ;
}
return len ;
}
/*
* The caller need check the return value . When the return value is
* nonzero , it means that some bytes have been transferred .
*
* If the fw_cfg file in question is smaller than the allocated & passed - in
* buffer , then the buffer has been partially written .
*
* If the fw_cfg file in question is larger than the passed - in
* buffer , then the return value explains how much was actually written .
*
* It is illegal to call this function if fw_cfg does not support DMA
* interface . The caller should ensure that DMA is supported before
* calling this function .
*
* Passed QOSState pointer qs must be initialized . qs - > alloc must also be
* properly initialized .
*/
size_t qfw_cfg_write_file ( QFWCFG * fw_cfg , QOSState * qs , const char * filename ,
void * data , size_t buflen )
{
uint32_t len = 0 ;
uint16_t sel ;
uint32_t id ;
g_assert ( qs ) ;
g_assert ( filename ) ;
g_assert ( data ) ;
g_assert ( buflen ) ;
/* write operation is only valid if DMA is supported */
id = qfw_cfg_get_u32 ( fw_cfg , FW_CFG_ID ) ;
g_assert ( id & FW_CFG_VERSION_DMA ) ;
if ( find_pdir_entry ( fw_cfg , filename , & sel , & len ) ) {
if ( len > buflen ) {
len = buflen ;
}
qfw_cfg_write_entry ( fw_cfg , qs , sel , data , len ) ;
}
return len ;
}
static void mm_fw_cfg_read ( QFWCFG * fw_cfg , void * data , size_t len )
{
uint8_t * ptr = data ;