forked from Qortal/Brooklyn
888 lines
28 KiB
C
888 lines
28 KiB
C
/*
|
|
Copyright (c) 2013, Broadcom Europe Ltd
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the copyright holder nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/**
|
|
*
|
|
* @file debug_sym.c
|
|
*
|
|
* @brief The usermode process which implements displays the messages.
|
|
*
|
|
****************************************************************************/
|
|
|
|
// ---- Include Files -------------------------------------------------------
|
|
|
|
#include "interface/vcos/vcos.h"
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#if defined(WIN32)
|
|
# include <io.h>
|
|
#elif defined(__CYGWIN__)
|
|
# include <sys/mman.h>
|
|
#else
|
|
# include <sys/mman.h>
|
|
# include <sys/ioctl.h>
|
|
# include <vc_mem.h>
|
|
#endif
|
|
|
|
#include "debug_sym.h"
|
|
#include "vc_debug_sym.h"
|
|
|
|
|
|
// ---- Public Variables ----------------------------------------------------
|
|
// ---- Private Constants and Types -----------------------------------------
|
|
|
|
#define MAX_VC_SIZE 8 /* a sanity upper bound on memory size */
|
|
|
|
#ifndef PAGE_SIZE
|
|
# if defined(__CYGWIN__)
|
|
# define PAGE_SIZE 65536 /* Cygwin mmap requires rounding to SYSTEM_INFO.dwAllocationGranularity */
|
|
# else
|
|
# define PAGE_SIZE 4096
|
|
# endif
|
|
#endif
|
|
#ifndef PAGE_MASK
|
|
# define PAGE_MASK (~(PAGE_SIZE - 1))
|
|
#endif
|
|
|
|
// Offset within the videocore memory map to get the address of the symbol
|
|
// table.
|
|
#define VC_SYMBOL_BASE_OFFSET VC_DEBUG_HEADER_OFFSET
|
|
|
|
struct opaque_vc_mem_access_handle_t
|
|
{
|
|
int memFd;
|
|
int memFdBase; /* The VideoCore address mapped to offset 0 of memFd */
|
|
VC_MEM_ADDR_T vcMemBase; /* The VideoCore address of the start of the loaded image */
|
|
VC_MEM_ADDR_T vcMemLoad; /* The VideoCore address of the start of the code image */
|
|
VC_MEM_ADDR_T vcMemEnd; /* The VideoCore address of the end of the loaded image */
|
|
VC_MEM_SIZE_T vcMemSize; /* The amount of memory used by the loaded image */
|
|
VC_MEM_ADDR_T vcMemPhys; /* The VideoCore memory physical address */
|
|
|
|
VC_MEM_ADDR_T vcSymbolTableOffset;
|
|
unsigned numSymbols;
|
|
VC_DEBUG_SYMBOL_T *symbol;
|
|
int use_vc_mem; /* using mmap-ed memory rather than real file */
|
|
};
|
|
|
|
#if 1
|
|
#define DBG( fmt, ... ) vcos_log_trace( "%s: " fmt, __FUNCTION__, ##__VA_ARGS__ )
|
|
#define ERR( fmt, ... ) vcos_log_error( "%s: " fmt, __FUNCTION__, ##__VA_ARGS__ )
|
|
#else
|
|
#define DBG( fmt, ... ) fprintf( stderr, "%s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__ )
|
|
#define ERR( fmt, ... ) fprintf( stderr, "%s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__ )
|
|
#endif
|
|
|
|
typedef enum
|
|
{
|
|
READ_MEM,
|
|
WRITE_MEM,
|
|
} MEM_OP_T;
|
|
|
|
#if defined( WIN32 )
|
|
#define open _open
|
|
#define close _close
|
|
#define O_SYNC 0
|
|
#endif
|
|
|
|
// ---- Private Variables ---------------------------------------------------
|
|
|
|
#define VCOS_LOG_CATEGORY (&debug_sym_log_category)
|
|
static VCOS_LOG_CAT_T debug_sym_log_category;
|
|
|
|
// ---- Private Function Prototypes -----------------------------------------
|
|
|
|
// ---- Functions -----------------------------------------------------------
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Get access to the videocore memory space. Returns zero if the memory was
|
|
* opened successfully.
|
|
*
|
|
***************************************************************************/
|
|
|
|
int OpenVideoCoreMemory( VC_MEM_ACCESS_HANDLE_T *vcHandlePtr )
|
|
{
|
|
return OpenVideoCoreMemoryFile( NULL, vcHandlePtr );
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Get access to the videocore memory space. Returns zero if the memory was
|
|
* opened successfully.
|
|
*
|
|
***************************************************************************/
|
|
|
|
struct fb_dmacopy {
|
|
void *dst;
|
|
uint32_t src;
|
|
uint32_t length;
|
|
};
|
|
#define FBIODMACOPY _IOW('z', 0x22, struct fb_dmacopy)
|
|
|
|
static int vc_mem_copy(void *dst, uint32_t src, uint32_t length)
|
|
{
|
|
const char *filename = "/dev/fb0";
|
|
int memFd;
|
|
struct fb_dmacopy ioparam;
|
|
|
|
ioparam.dst = dst;
|
|
ioparam.src = src;
|
|
ioparam.length = length;
|
|
|
|
if (( memFd = open( filename, O_RDWR | O_SYNC )) < 0 )
|
|
{
|
|
ERR( "Unable to open '%s': %s(%d)\n", filename, strerror( errno ), errno );
|
|
return -errno;
|
|
}
|
|
|
|
if ( ioctl( memFd, FBIODMACOPY, &ioparam ) != 0 )
|
|
{
|
|
close( memFd );
|
|
return -errno;
|
|
}
|
|
close( memFd );
|
|
return 0;
|
|
}
|
|
|
|
int OpenVideoCoreMemoryFile( const char *filename, VC_MEM_ACCESS_HANDLE_T *vcHandlePtr )
|
|
{
|
|
return OpenVideoCoreMemoryFileWithOffset( filename, vcHandlePtr, 0 );
|
|
}
|
|
|
|
int OpenVideoCoreMemoryFileWithOffsetAndSize( const char *filename, VC_MEM_ACCESS_HANDLE_T *vcHandlePtr, size_t loadOffset, size_t loadSize )
|
|
{
|
|
int rc = 0;
|
|
VC_MEM_ACCESS_HANDLE_T newHandle;
|
|
VC_DEBUG_SYMBOL_T debug_sym;
|
|
VC_MEM_ADDR_T symAddr;
|
|
size_t symTableSize;
|
|
unsigned symIdx;
|
|
|
|
struct
|
|
{
|
|
VC_DEBUG_HEADER_T header;
|
|
VC_DEBUG_PARAMS_T params;
|
|
|
|
} vc_dbg;
|
|
|
|
vcos_log_register( "debug_sym", &debug_sym_log_category );
|
|
|
|
if (( newHandle = calloc( 1, sizeof( *newHandle ))) == NULL )
|
|
{
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if ( filename == NULL )
|
|
{
|
|
filename = "/dev/vc-mem";
|
|
newHandle->memFd = open( filename, O_RDWR | O_SYNC );
|
|
|
|
if ( newHandle->memFd >= 0 )
|
|
{
|
|
newHandle->use_vc_mem = 1;
|
|
}
|
|
else
|
|
{
|
|
if ( !loadOffset && !loadSize )
|
|
{
|
|
//Search /proc/cmdline for the vc_mem base and size params
|
|
int cmdlineFd;
|
|
|
|
cmdlineFd = open( "/proc/cmdline", O_RDONLY );
|
|
if ( cmdlineFd >= 0 )
|
|
{
|
|
char cmdline[1024]; //Should be sufficient for most use cases
|
|
char *vc_mem_ptr = cmdline;
|
|
size_t size;
|
|
|
|
size = read(cmdlineFd, cmdline, sizeof(cmdline));
|
|
|
|
while ( (vc_mem_ptr = strstr(vc_mem_ptr, "vc_mem.")) != NULL)
|
|
{
|
|
if ( !strncmp(&vc_mem_ptr[7], "mem_base=", 9) )
|
|
{
|
|
loadOffset = ( size_t )strtoul( &vc_mem_ptr[7+9], NULL, 0);
|
|
}
|
|
else if ( !strncmp(&vc_mem_ptr[7], "mem_size=", 9) )
|
|
{
|
|
loadSize = ( size_t )strtoul( &vc_mem_ptr[7+9], NULL, 0);
|
|
DBG("loadSize %x\n", loadSize);
|
|
}
|
|
vc_mem_ptr++;
|
|
}
|
|
close( cmdlineFd );
|
|
}
|
|
}
|
|
|
|
// Try /dev/mem
|
|
filename = "/dev/mem";
|
|
newHandle->memFd = open( filename, O_RDWR | O_SYNC );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newHandle->use_vc_mem = 0;
|
|
newHandle->memFd = open( filename, O_RDONLY | O_SYNC );
|
|
}
|
|
|
|
if ( newHandle->memFd < 0 )
|
|
{
|
|
ERR( "Unable to open '%s': %s(%d)\n", filename, strerror( errno ), errno );
|
|
free(newHandle);
|
|
return -errno;
|
|
}
|
|
DBG( "Opened %s memFd = %d", filename, newHandle->memFd );
|
|
|
|
if ( newHandle->use_vc_mem )
|
|
{
|
|
newHandle->memFdBase = 0;
|
|
|
|
#if defined(WIN32) || defined(__CYGWIN__)
|
|
#define VC_MEM_SIZE (128 * 1024 * 1024)
|
|
newHandle->vcMemSize = VC_MEM_SIZE;
|
|
newHandle->vcMemBase = 0;
|
|
newHandle->vcMemLoad = 0;
|
|
newHandle->vcMemPhys = 0;
|
|
#else
|
|
if ( ioctl( newHandle->memFd, VC_MEM_IOC_MEM_SIZE, &newHandle->vcMemSize ) != 0 )
|
|
{
|
|
ERR( "Failed to get memory size via ioctl: %s(%d)\n",
|
|
strerror( errno ), errno );
|
|
free(newHandle);
|
|
return -errno;
|
|
}
|
|
if ( ioctl( newHandle->memFd, VC_MEM_IOC_MEM_BASE, &newHandle->vcMemBase ) != 0 )
|
|
{
|
|
ERR( "Failed to get memory base via ioctl: %s(%d)\n",
|
|
strerror( errno ), errno );
|
|
free(newHandle);
|
|
return -errno;
|
|
}
|
|
if ( ioctl( newHandle->memFd, VC_MEM_IOC_MEM_LOAD, &newHandle->vcMemLoad ) != 0 )
|
|
{
|
|
ERR( "Failed to get memory load via ioctl: %s(%d)\n",
|
|
strerror( errno ), errno );
|
|
/* Backward compatibility. */
|
|
newHandle->vcMemLoad = newHandle->vcMemBase;
|
|
}
|
|
if ( ioctl( newHandle->memFd, VC_MEM_IOC_MEM_PHYS_ADDR, &newHandle->vcMemPhys ) != 0 )
|
|
{
|
|
ERR( "Failed to get memory physical address via ioctl: %s(%d)\n",
|
|
strerror( errno ), errno );
|
|
free(newHandle);
|
|
return -errno;
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
off_t len;
|
|
if ( !loadSize )
|
|
{
|
|
len = lseek( newHandle->memFd, 0, SEEK_END );
|
|
if ( len < 0 )
|
|
{
|
|
ERR( "Failed to seek to end of file: %s(%d)\n", strerror( errno ), errno );
|
|
free(newHandle);
|
|
return -errno;
|
|
}
|
|
}
|
|
else
|
|
len = loadSize;
|
|
|
|
newHandle->vcMemPhys = 0;
|
|
newHandle->vcMemSize = len;
|
|
newHandle->vcMemBase = 0;
|
|
newHandle->vcMemLoad = loadOffset;
|
|
newHandle->memFdBase = 0;
|
|
}
|
|
|
|
DBG( "vcMemSize = %08x", newHandle->vcMemSize );
|
|
DBG( "vcMemBase = %08x", newHandle->vcMemBase );
|
|
DBG( "vcMemLoad = %08x", newHandle->vcMemLoad );
|
|
DBG( "vcMemPhys = %08x", newHandle->vcMemPhys );
|
|
|
|
newHandle->vcMemEnd = newHandle->vcMemBase + newHandle->vcMemSize - 1;
|
|
|
|
// See if we can detect the symbol table
|
|
if ( !ReadVideoCoreMemory( newHandle,
|
|
&newHandle->vcSymbolTableOffset,
|
|
newHandle->vcMemLoad + VC_SYMBOL_BASE_OFFSET,
|
|
sizeof( newHandle->vcSymbolTableOffset )))
|
|
{
|
|
ERR( "ReadVideoCoreMemory @VC_SYMBOL_BASE_OFFSET (0x%08x) failed\n", VC_SYMBOL_BASE_OFFSET );
|
|
rc = -EIO;
|
|
goto err_exit;
|
|
}
|
|
|
|
// When reading from a file, the VC binary is read into a buffer and the effective base is 0.
|
|
// But that may not be the actual base address the binary is intended to be loaded to. The
|
|
// following reads the debug header to find out what the actual base address is.
|
|
if( !newHandle->use_vc_mem )
|
|
{
|
|
// Read the complete debug header
|
|
if ( !ReadVideoCoreMemory( newHandle,
|
|
&vc_dbg,
|
|
newHandle->vcMemLoad + VC_SYMBOL_BASE_OFFSET,
|
|
sizeof( vc_dbg )))
|
|
{
|
|
ERR( "ReadVideoCoreMemory @VC_SYMBOL_BASE_OFFSET (0x%08x) failed\n", VC_SYMBOL_BASE_OFFSET );
|
|
rc = -EIO;
|
|
goto err_exit;
|
|
}
|
|
// The vc_dbg header gives the "base" address of the VC binary,
|
|
// which debug_sym calls the "load" address, so we need to adjust
|
|
// it by loadOffset to find the base of the whole memory dump file
|
|
newHandle->memFdBase = vc_dbg.params.vcMemBase - loadOffset;
|
|
newHandle->vcMemBase = vc_dbg.params.vcMemBase - loadOffset;
|
|
newHandle->vcMemLoad = vc_dbg.params.vcMemBase;
|
|
newHandle->vcMemEnd = newHandle->memFdBase + newHandle->vcMemSize - 1;
|
|
|
|
DBG( "Updated from debug header:" );
|
|
DBG( "vcMemSize = %08x", newHandle->vcMemSize );
|
|
DBG( "vcMemBase = %08x", newHandle->vcMemBase );
|
|
DBG( "vcMemLoad = %08x", newHandle->vcMemLoad );
|
|
DBG( "vcMemPhys = %08x", newHandle->vcMemPhys );
|
|
}
|
|
|
|
DBG( "vcSymbolTableOffset = 0x%08x", newHandle->vcSymbolTableOffset );
|
|
|
|
// Make sure that the pointer points into the first few megabytes of
|
|
// the memory space.
|
|
if ( (newHandle->vcSymbolTableOffset - newHandle->vcMemLoad) > ( MAX_VC_SIZE * 1024 * 1024 ))
|
|
{
|
|
ERR( "newHandle->vcSymbolTableOffset (0x%x - 0x%x) > %dMB\n", newHandle->vcSymbolTableOffset, newHandle->vcMemLoad, MAX_VC_SIZE );
|
|
rc = -EIO;
|
|
goto err_exit;
|
|
}
|
|
|
|
// Make a pass to count how many symbols there are.
|
|
|
|
symAddr = newHandle->vcSymbolTableOffset;
|
|
newHandle->numSymbols = 0;
|
|
do
|
|
{
|
|
if ( !ReadVideoCoreMemory( newHandle,
|
|
&debug_sym,
|
|
symAddr,
|
|
sizeof( debug_sym )))
|
|
{
|
|
ERR( "ReadVideoCoreMemory @ symAddr(0x%08x) failed\n", symAddr );
|
|
rc = -EIO;
|
|
goto err_exit;
|
|
}
|
|
|
|
newHandle->numSymbols++;
|
|
|
|
DBG( "Symbol %d: label: 0x%p addr: 0x%08x size: %zu",
|
|
newHandle->numSymbols,
|
|
debug_sym.label,
|
|
debug_sym.addr,
|
|
debug_sym.size );
|
|
|
|
if ( newHandle->numSymbols > 1024 )
|
|
{
|
|
// Something isn't sane.
|
|
|
|
ERR( "numSymbols (%d) > 1024 - looks wrong\n", newHandle->numSymbols );
|
|
rc = -EIO;
|
|
goto err_exit;
|
|
}
|
|
symAddr += sizeof( debug_sym );
|
|
|
|
} while ( debug_sym.label != 0 );
|
|
newHandle->numSymbols--;
|
|
|
|
DBG( "Detected %d symbols", newHandle->numSymbols );
|
|
|
|
// Allocate some memory to hold the symbols, and read them in.
|
|
|
|
symTableSize = newHandle->numSymbols * sizeof( debug_sym );
|
|
if (( newHandle->symbol = malloc( symTableSize )) == NULL )
|
|
{
|
|
rc = -ENOMEM;
|
|
goto err_exit;
|
|
}
|
|
if ( !ReadVideoCoreMemory( newHandle,
|
|
newHandle->symbol,
|
|
newHandle->vcSymbolTableOffset,
|
|
symTableSize ))
|
|
{
|
|
ERR( "ReadVideoCoreMemory @ newHandle->vcSymbolTableOffset(0x%08x) failed\n", newHandle->vcSymbolTableOffset );
|
|
rc = -EIO;
|
|
goto err_exit;
|
|
}
|
|
|
|
// The names of the symbols are pointers in videocore space. We want
|
|
// to have them available locally, so we make copies and fixup
|
|
// the pointer.
|
|
|
|
for ( symIdx = 0; symIdx < newHandle->numSymbols; symIdx++ )
|
|
{
|
|
VC_DEBUG_SYMBOL_T *sym;
|
|
char symName[ 256 ];
|
|
|
|
sym = &newHandle->symbol[ symIdx ];
|
|
|
|
DBG( "Symbol %d: label: 0x%p addr: 0x%08x size: %zu",
|
|
symIdx,
|
|
sym->label,
|
|
sym->addr,
|
|
sym->size );
|
|
|
|
if ( !ReadVideoCoreMemory( newHandle,
|
|
symName,
|
|
TO_VC_MEM_ADDR(sym->label),
|
|
sizeof( symName )))
|
|
{
|
|
ERR( "ReadVideoCoreMemory @ sym->label(0x%08x) failed\n", TO_VC_MEM_ADDR(sym->label) );
|
|
rc = -EIO;
|
|
goto err_exit;
|
|
}
|
|
symName[ sizeof( symName ) - 1 ] = '\0';
|
|
sym->label = vcos_strdup( symName );
|
|
|
|
DBG( "Symbol %d (@0x%p): label: '%s' addr: 0x%08x size: %zu",
|
|
symIdx,
|
|
sym,
|
|
sym->label,
|
|
sym->addr,
|
|
sym->size );
|
|
}
|
|
|
|
*vcHandlePtr = newHandle;
|
|
return 0;
|
|
|
|
err_exit:
|
|
close( newHandle->memFd );
|
|
free( newHandle );
|
|
|
|
return rc;
|
|
}
|
|
|
|
int OpenVideoCoreMemoryFileWithOffset( const char *filename, VC_MEM_ACCESS_HANDLE_T *vcHandlePtr, size_t loadOffset)
|
|
{
|
|
return OpenVideoCoreMemoryFileWithOffsetAndSize( filename, vcHandlePtr, loadOffset, 0 );
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Returns the number of symbols which were detected.
|
|
*
|
|
***************************************************************************/
|
|
|
|
unsigned NumVideoCoreSymbols( VC_MEM_ACCESS_HANDLE_T vcHandle )
|
|
{
|
|
return vcHandle->numSymbols;
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Returns the name, address and size of the i'th symbol.
|
|
*
|
|
***************************************************************************/
|
|
|
|
int GetVideoCoreSymbol( VC_MEM_ACCESS_HANDLE_T vcHandle, unsigned idx, char *labelBuf, size_t labelBufSize, VC_MEM_ADDR_T *vcMemAddr, size_t *vcMemSize )
|
|
{
|
|
VC_DEBUG_SYMBOL_T *sym;
|
|
|
|
if ( idx >= vcHandle->numSymbols )
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
sym = &vcHandle->symbol[ idx ];
|
|
|
|
strncpy( labelBuf, sym->label, labelBufSize );
|
|
labelBuf[labelBufSize - 1] = '\0';
|
|
|
|
if ( vcMemAddr != NULL )
|
|
{
|
|
*vcMemAddr = (VC_MEM_ADDR_T)sym->addr;
|
|
}
|
|
if ( vcMemSize != NULL )
|
|
{
|
|
*vcMemSize = sym->size;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Looks up the named, symbol. If the symbol is found, it's value and size
|
|
* are returned.
|
|
*
|
|
* Returns true if the lookup was successful.
|
|
*
|
|
***************************************************************************/
|
|
|
|
int LookupVideoCoreSymbol( VC_MEM_ACCESS_HANDLE_T vcHandle, const char *symbol, VC_MEM_ADDR_T *vcMemAddr, size_t *vcMemSize )
|
|
{
|
|
unsigned idx;
|
|
char symName[ 64 ];
|
|
VC_MEM_ADDR_T symAddr = 0;
|
|
size_t symSize = 0;
|
|
|
|
for ( idx = 0; idx < vcHandle->numSymbols; idx++ )
|
|
{
|
|
GetVideoCoreSymbol( vcHandle, idx, symName, sizeof( symName ), &symAddr, &symSize );
|
|
if ( strcmp( symbol, symName ) == 0 )
|
|
{
|
|
if ( vcMemAddr != NULL )
|
|
{
|
|
*vcMemAddr = symAddr;
|
|
}
|
|
if ( vcMemSize != 0 )
|
|
{
|
|
*vcMemSize = symSize;
|
|
}
|
|
|
|
DBG( "%s found, addr = 0x%08x size = %zu", symbol, symAddr, symSize );
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if ( vcMemAddr != NULL )
|
|
{
|
|
*vcMemAddr = 0;
|
|
}
|
|
if ( vcMemSize != 0 )
|
|
{
|
|
*vcMemSize = 0;
|
|
}
|
|
DBG( "%s not found", symbol );
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Looks up the named, symbol. If the symbol is found, and it's size is equal
|
|
* to the sizeof a uint32_t, then true is returned.
|
|
*
|
|
***************************************************************************/
|
|
|
|
int LookupVideoCoreUInt32Symbol( VC_MEM_ACCESS_HANDLE_T vcHandle,
|
|
const char *symbol,
|
|
VC_MEM_ADDR_T *vcMemAddr )
|
|
{
|
|
size_t vcMemSize;
|
|
|
|
if ( !LookupVideoCoreSymbol( vcHandle, symbol, vcMemAddr, &vcMemSize ))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if ( vcMemSize != sizeof( uint32_t ))
|
|
{
|
|
ERR( "Symbol: '%s' has a size of %zu, expecting %zu", symbol, vcMemSize, sizeof( uint32_t ));
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Does Reads or Writes on the videocore memory.
|
|
*
|
|
***************************************************************************/
|
|
|
|
static int AccessVideoCoreMemory( VC_MEM_ACCESS_HANDLE_T vcHandle,
|
|
MEM_OP_T mem_op,
|
|
void *buf,
|
|
VC_MEM_ADDR_T vcMemAddr,
|
|
size_t numBytes )
|
|
{
|
|
VC_MEM_ADDR_T origVcMemAddr = vcMemAddr;
|
|
DBG( "%s %zu bytes @ 0x%08x", mem_op == WRITE_MEM ? "Write" : "Read", numBytes, vcMemAddr );
|
|
|
|
/*
|
|
* Since we'll be passed videocore pointers, we need to deal with the high bits.
|
|
*
|
|
* We need to strip off the high 2 bits to convert to a physical address, except
|
|
* for when the high 3 bits are equal to 011, which means that it corresponds to
|
|
* a peripheral and isn't accessible.
|
|
*/
|
|
|
|
if ( IS_ALIAS_PERIPHERAL( vcMemAddr ))
|
|
{
|
|
// This is a peripheral address.
|
|
|
|
ERR( "Can't access peripheral address 0x%08x", vcMemAddr );
|
|
return 0;
|
|
}
|
|
vcMemAddr = TO_VC_MEM_ADDR(ALIAS_NORMAL( vcMemAddr ));
|
|
|
|
#if 0
|
|
if ( (vcMemAddr < vcHandle->vcMemBase) ||
|
|
(vcMemAddr > vcHandle->vcMemEnd) )
|
|
{
|
|
ERR( "Memory address 0x%08x is outside range 0x%08x-0x%08x", vcMemAddr,
|
|
vcHandle->vcMemBase, vcHandle->vcMemEnd );
|
|
return 0;
|
|
}
|
|
#endif
|
|
if (( vcMemAddr + numBytes - 1) > vcHandle->vcMemEnd )
|
|
{
|
|
ERR( "Memory address 0x%08x + numBytes 0x%08zx is > memory end 0x%08x",
|
|
vcMemAddr, numBytes, vcHandle->vcMemEnd );
|
|
return 0;
|
|
}
|
|
|
|
vcMemAddr -= vcHandle->memFdBase;
|
|
|
|
#if defined( WIN32 )
|
|
if ( mem_op != READ_MEM )
|
|
{
|
|
ERR( "Only reads are supported" );
|
|
return 0;
|
|
}
|
|
if ( _lseek( vcHandle->memFd, vcMemAddr, SEEK_SET ) < 0 )
|
|
{
|
|
ERR( "_lseek position 0x%08x failed", vcMemAddr );
|
|
return 0;
|
|
}
|
|
if ( _read( vcHandle->memFd, buf, numBytes ) < 0 )
|
|
{
|
|
ERR( "_read failed: %s(%d)", strerror( errno ), errno );
|
|
return 0;
|
|
}
|
|
#else
|
|
// DMA allows memory to be accessed above 1008M and is more coherent so try this first
|
|
if (mem_op == READ_MEM && vcHandle->use_vc_mem)
|
|
{
|
|
DBG( "AccessVideoCoreMemory: %p, %x, %d", buf, origVcMemAddr, numBytes );
|
|
int s = vc_mem_copy(buf, (uint32_t)origVcMemAddr, numBytes);
|
|
if (s == 0)
|
|
return 1;
|
|
}
|
|
|
|
{
|
|
uint8_t *mapAddr;
|
|
size_t mapSize;
|
|
size_t memOffset;
|
|
off_t vcMapAddr;
|
|
int mmap_prot;
|
|
|
|
if ( mem_op == WRITE_MEM )
|
|
{
|
|
mmap_prot = PROT_WRITE;
|
|
}
|
|
else
|
|
{
|
|
mmap_prot = PROT_READ;
|
|
}
|
|
|
|
// We can only map pages on 4K boundaries, so round the address down and the size up.
|
|
|
|
memOffset = vcMemAddr & ~PAGE_MASK;
|
|
|
|
vcMapAddr = vcMemAddr & PAGE_MASK;
|
|
|
|
mapSize = ( memOffset + numBytes + PAGE_SIZE - 1 ) & PAGE_MASK;
|
|
if (( mapAddr = mmap( 0, mapSize, mmap_prot, MAP_SHARED, vcHandle->memFd, vcMapAddr )) == MAP_FAILED )
|
|
{
|
|
ERR( "mmap failed: %s(%d)", strerror( errno ), errno );
|
|
return 0;
|
|
}
|
|
if ( mem_op == WRITE_MEM )
|
|
{
|
|
memcpy( mapAddr + memOffset, buf, numBytes );
|
|
}
|
|
else
|
|
{
|
|
memcpy( buf, mapAddr + memOffset, numBytes );
|
|
}
|
|
|
|
munmap( mapAddr, mapSize );
|
|
}
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Reads 'numBytes' from the videocore memory starting at 'vcMemAddr'. The
|
|
* results are stored in 'buf'.
|
|
*
|
|
* Returns true if the read was successful.
|
|
*
|
|
***************************************************************************/
|
|
|
|
int ReadVideoCoreMemory( VC_MEM_ACCESS_HANDLE_T vcHandle, void *buf, VC_MEM_ADDR_T vcMemAddr, size_t numBytes )
|
|
{
|
|
return AccessVideoCoreMemory( vcHandle, READ_MEM, buf, vcMemAddr, numBytes );
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Reads 'numBytes' from the videocore memory starting at 'vcMemAddr'. The
|
|
* results are stored in 'buf'.
|
|
*
|
|
* Returns true if the read was successful.
|
|
*
|
|
***************************************************************************/
|
|
|
|
int ReadVideoCoreMemoryBySymbol( VC_MEM_ACCESS_HANDLE_T vcHandle, const char *symbol, void *buf, size_t bufSize )
|
|
{
|
|
VC_MEM_ADDR_T vcMemAddr;
|
|
size_t vcMemSize;
|
|
|
|
if ( !LookupVideoCoreSymbol( vcHandle, symbol, &vcMemAddr, &vcMemSize ))
|
|
{
|
|
ERR( "Symbol not found: '%s'", symbol );
|
|
return 0;
|
|
}
|
|
|
|
if ( vcMemSize > bufSize )
|
|
{
|
|
vcMemSize = bufSize;
|
|
}
|
|
|
|
if ( !ReadVideoCoreMemory( vcHandle, buf, vcMemAddr, vcMemSize ))
|
|
{
|
|
ERR( "Unable to read %zu bytes @ 0x%08x", vcMemSize, vcMemAddr );
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Looks up a symbol and reads the contents into a user supplied buffer.
|
|
*
|
|
* Returns true if the read was successful.
|
|
*
|
|
***************************************************************************/
|
|
|
|
int ReadVideoCoreStringBySymbol( VC_MEM_ACCESS_HANDLE_T vcHandle,
|
|
const char *symbol,
|
|
char *buf,
|
|
size_t bufSize )
|
|
{
|
|
VC_MEM_ADDR_T vcMemAddr;
|
|
size_t vcMemSize;
|
|
|
|
if ( !LookupVideoCoreSymbol( vcHandle, symbol, &vcMemAddr, &vcMemSize ))
|
|
{
|
|
ERR( "Symbol not found: '%s'", symbol );
|
|
return 0;
|
|
}
|
|
|
|
if ( vcMemSize > bufSize )
|
|
{
|
|
vcMemSize = bufSize;
|
|
}
|
|
|
|
if ( !ReadVideoCoreMemory( vcHandle, buf, vcMemAddr, vcMemSize ))
|
|
{
|
|
ERR( "Unable to read %zu bytes @ 0x%08x", vcMemSize, vcMemAddr );
|
|
return 0;
|
|
}
|
|
|
|
// Make sure that the result is null-terminated
|
|
|
|
buf[vcMemSize-1] = '\0';
|
|
return 1;
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Writes 'numBytes' into the videocore memory starting at 'vcMemAddr'. The
|
|
* data is taken from 'buf'.
|
|
*
|
|
* Returns true if the write was successful.
|
|
*
|
|
***************************************************************************/
|
|
|
|
int WriteVideoCoreMemory( VC_MEM_ACCESS_HANDLE_T vcHandle,
|
|
void *buf,
|
|
VC_MEM_ADDR_T vcMemAddr,
|
|
size_t numBytes )
|
|
{
|
|
return AccessVideoCoreMemory( vcHandle, WRITE_MEM, buf, vcMemAddr, numBytes );
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Closes the memory space opened previously via OpenVideoCoreMemory.
|
|
*
|
|
***************************************************************************/
|
|
|
|
void CloseVideoCoreMemory( VC_MEM_ACCESS_HANDLE_T vcHandle )
|
|
{
|
|
unsigned i;
|
|
if ( vcHandle->symbol )
|
|
for ( i = 0; i < vcHandle->numSymbols; i++ )
|
|
free( (char *)vcHandle->symbol[i].label );
|
|
free( vcHandle->symbol );
|
|
|
|
if ( vcHandle->memFd >= 0 )
|
|
close( vcHandle->memFd );
|
|
|
|
free( vcHandle );
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Returns the base address of the videocore memory space.
|
|
*
|
|
***************************************************************************/
|
|
|
|
VC_MEM_ADDR_T GetVideoCoreMemoryBase( VC_MEM_ACCESS_HANDLE_T vcHandle )
|
|
{
|
|
return vcHandle->vcMemBase;
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Returns the size of the videocore memory space.
|
|
*
|
|
***************************************************************************/
|
|
|
|
VC_MEM_SIZE_T GetVideoCoreMemorySize( VC_MEM_ACCESS_HANDLE_T vcHandle )
|
|
{
|
|
return vcHandle->vcMemSize;
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Returns the videocore memory physical address.
|
|
*
|
|
***************************************************************************/
|
|
|
|
VC_MEM_ADDR_T GetVideoCoreMemoryPhysicalAddress( VC_MEM_ACCESS_HANDLE_T vcHandle )
|
|
{
|
|
return vcHandle->vcMemPhys;
|
|
}
|
|
|