Driver Development Kit (DDK)

cancel
Showing results for 
Search instead for 
Did you mean: 

osiUserCode for VISA and 64bit builds

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 );
}
0 Kudos
Message 1 of 1
(5,232 Views)