03-08-2016 03:08 AM
The osiUserCode.cpp supplied by NI invokes undefined behaviour for 64bit builds, since it uses 32bit integers (4 bytes) to store addresses (8 bytes) returned by viGetAttribute and the likes, resulting in stack corruption.
I don't know how and who to contact at NI to get this fixed so I'm posting a workaround here; fix is just using size_t for storage, and a checked cast when the address needs converting to 32bit.
/*
* VISA/osiUserCode.cpp (for VISA)
* osiUserCode.cpp holds the two user defined functions needed to port iBus
* to a target platform.
*
* iBus* acquireBoard(char*brdLocation) -- constructs and initializes the iBus
* void releaseBoard(iBus *&bus) -- deletes and cleans up the iBus
*
* Copyright 2011 National Instruments
* License: NATIONAL INSTRUMENTS SOFTWARE LICENSE AGREEMENT
* Refer to "MHDDK License Agreement.pdf" in the root of this distribution.
*
*/
#include <limits>
#include <stdexcept>
#include <cassert>
// Platform independent headers
#include "osiBus.h"
// VISA specific headers
#define NIVISA_PXI
#include "visa.h"
#ifdef _WIN64
u32 adress_cast( size_t p )
{
static_assert( sizeof( u32 ) < sizeof( size_t ), "incorrect size_t size" );
if( p > std::numeric_limits< u32 >::max() )
{
const auto message = "cannot safely cast 64bit address to 32bit address";
assert( 0 && message );
throw std::runtime_error( message );
}
return static_cast< u32 >( p );
}
#else
u32 adress_cast( size_t p )
{
static_assert( sizeof( u32 ) == sizeof( size_t ), "incorrect size_t size" );
return p;
}
#endif
struct tVisaSpecific
{
ViSession sesn;
ViSession vi1;
#ifndef kBAR0Only
ViSession vi2;
#endif
};
iBus* acquireBoard( tChar* brdLocation )
{
iBus* bus;
size_t devBAR0;
size_t BAR0sz;
void* mem0;
#ifndef kBAR0Only
u32 devBAR1;
u32 BAR1sz;
void* mem1;
#endif
ViSession sesn;
ViSession vi1;
#ifndef kBAR0Only
ViSession vi2;
#endif
ViStatus status;
ViUInt32 openTimeout = 1000; // msec
ViAccessMode accessMode = VI_NULL;
status = viOpenDefaultRM( &sesn );
if( status < 0 )
{
return 0;
}
status = viOpen( sesn, brdLocation, accessMode, openTimeout, &vi1 );
if( status < 0 )
{
return 0;
}
// Extract physical BARs for card
status = viGetAttribute( vi1, VI_ATTR_PXI_MEM_BASE_BAR0, &devBAR0 );
status = viGetAttribute( vi1, VI_ATTR_PXI_MEM_SIZE_BAR0, &BAR0sz );
// Memory map the PCI card
status = viMapAddress( vi1, VI_PXI_BAR0_SPACE, 0, BAR0sz, 0, 0, &mem0 );
if( status < 0 )
{
return 0;
}
#ifndef kBAR0Only
status = viGetAttribute( vi1, VI_ATTR_PXI_MEM_BASE_BAR1, &devBAR1 );
status = viGetAttribute( vi1, VI_ATTR_PXI_MEM_SIZE_BAR1, &BAR1sz );
// VISA only allows one mmap for each session, so we
// open the resource a second time to get to BAR1
status = viOpen( sesn, brdLocation, accessMode, openTimeout, &vi2 );
status = viMapAddress( vi2, VI_PXI_BAR1_SPACE, 0, BAR1sz, 0, 0, &mem1 );
if( status < 0 )
{
return 0;
}
#endif
// Create a new iBus
#ifndef kBAR0Only
bus = new iBus( 0, 0, mem0, mem1 );
#else
bus = new iBus( 0, 0, mem0, NULL );
#endif
bus->_physBar[ 0 ] = adress_cast( devBAR0 );
#ifndef kBAR0Only
bus->_physBar[ 1 ] = adress_cast( devBAR1 );
#else
bus->_physBar[ 1 ] = 0;
#endif
bus->_physBar[ 2 ] = 0;
bus->_physBar[ 3 ] = 0;
bus->_physBar[ 4 ] = 0;
bus->_physBar[ 5 ] = 0;
tVisaSpecific* specific = new tVisaSpecific;
specific->sesn = sesn;
specific->vi1 = vi1;
#ifndef kBAR0Only
specific->vi2 = vi2;
#endif
bus->_osSpecific = reinterpret_cast< void* >( specific );
return bus;
}
void releaseBoard( iBus*& bus )
{
tVisaSpecific* specific = reinterpret_cast< tVisaSpecific* >( bus->_osSpecific );
#ifndef kBAR0Only
viUnmapAddress( specific->vi2 );
#endif
viUnmapAddress( specific->vi1 );
viClose( specific->vi1 );
#ifndef kBAR0Only
viClose( specific->vi2 );
#endif
viClose( specific->sesn );
delete specific;
delete bus;
}
//
// DMA Memory support
//
class tVISADMAMemory : public tDMAMemory
{
public:
tVISADMAMemory( void* vAddress, size_t pAddress, u32 size, ViSession vi )
: tDMAMemory( vAddress, adress_cast( pAddress ), size ),
_vi( vi )
{
}
~tVISADMAMemory();
inline ViSession getVisaSession( void )
{
return _vi;
}
private:
ViSession _vi;
};
tDMAMemory* iBus::allocDMA( u32 size )
{
ViStatus status;
ViSession vi;
tVisaSpecific* specific = reinterpret_cast< tVisaSpecific* >( _osSpecific );
size_t physicalAddress;
void* virtualAddress;
status = viOpen( specific->sesn, "pxi::memacc", VI_NO_LOCK, 100, &vi );
if( status != VI_SUCCESS )
{
return NULL;
}
status = viMemAlloc( vi, size, (ViPBusAddress) &physicalAddress );
if( status != VI_SUCCESS )
{
viClose( vi );
return 0;
}
status = viMapAddress( vi, VI_PXI_ALLOC_SPACE, physicalAddress, size, VI_NO_LOCK, VI_NULL, &virtualAddress );
if( status != VI_SUCCESS )
{
viMemFree( vi, physicalAddress );
viClose( vi );
return 0;
}
tDMAMemory* dma = new tVISADMAMemory( virtualAddress, physicalAddress, size, vi );
if( NULL == dma )
{
viUnmapAddress( vi );
viMemFree( vi, physicalAddress );
viClose( vi );
}
return dma;
}
void iBus::freeDMA( tDMAMemory* mem )
{
delete mem;
}
//
// tVISADMAMemory
//
tVISADMAMemory::~tVISADMAMemory()
{
viUnmapAddress( _vi );
viMemFree( _vi, getPhysicalAddress() );
viClose( _vi );
}