Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / devices / ioram / include / sparse_mem.h
/*
* ========== Copyright Header Begin ==========================================
*
* OpenSPARC T2 Processor File: sparse_mem.h
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
*
* The above named program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License version 2 as published by the Free Software Foundation.
*
* The above named program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ========== Copyright Header End ============================================
*/
#ifndef __IOMEM_SPARSE_MEM_H__
#define __IOMEM_SPARSE_MEM_H__
#include <synch.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <libelf.h>
#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include "mem_intf.h"
#include "utils.h"
#if defined(ARCH_X64)
#define switch_endian16(a) (ss_byteswap16(a))
#define switch_endian32(a) (ss_byteswap32(a))
#define switch_endian64(a) (ss_byteswap64(a))
#else
#define switch_endian16(a) (a)
#define switch_endian32(a) (a)
#define switch_endian64(a) (a)
#endif
// sparse memory model
class SparseMemory:public ioMemIntf/*{{{*/
{
char * name;
void SparseMemory::load_elf64_section(Elf64_Shdr *shdr, Elf_Scn *scn,
uint64_t base_pa, uint64_t base_va);
static const int SAM_NMEM_LOCKS = 1<<3;
// 8, should be power of 2
// maintain coherence at 8 byte level only
public:
SparseMemory( uint64_t ram_size=0, uint_t _l1bits=18, uint_t _l2bits=10, uint_t _l3bits=20 );
~SparseMemory();
void setId(const char * n){ name = strdup(n); }
uint64_t get_size() { return size; }
// get_base() is a private method and should not be used
// outside this module
uint8_t* get_base() { return NULL; }
int load_img( const char* mem_image_filename, uint64_t start_pa );
int load_bin( const char *file, uint64_t addr);
int load_elf( const char *filename, uint64_t base_pa );
int save( const char* filename, uint64_t addr, uint64_t size );
// Supported Store Operations. st8(), st16(), st32() and st64() are gueranteed to be atomic.
// st128() and st512() are atomic per 64bit quantity.
void st8( uint64_t addr, uint8_t data )
{
*(uint8_t*)(get_st_ptr(addr)) = data;
}
void st16( uint64_t addr, uint16_t data )
{
*(uint16_t*)(get_st_ptr(addr)) = switch_endian16(data);
}
void st32( uint64_t addr, uint32_t data )
{
*(uint32_t*)(get_st_ptr(addr)) = switch_endian32(data);
}
void st64_nl( uint64_t addr, uint64_t data )
{
*(uint64_t*)(get_st_ptr(addr)) = switch_endian64(data);
}
void st64 ( uint64_t addr, uint64_t data )
{
lock(addr);
st64_nl(addr,switch_endian64(data));
unlock(addr);
}
void st128( uint64_t addr, uint64_t data[2] )
{
uint8_t* ptr = get_st_ptr(addr & ~uint64_t(0x1f));
*(uint64_t*)(ptr + 0) = switch_endian64(data[0]);
*(uint64_t*)(ptr + 8) = switch_endian64(data[1]);
}
void st512( uint64_t addr, uint64_t data[8] )
{
uint8_t* ptr = get_st_ptr(addr & ~uint64_t(0x3f));
*(uint64_t*)(ptr + 0) = switch_endian64(data[0]);
*(uint64_t*)(ptr + 8) = switch_endian64(data[1]);
*(uint64_t*)(ptr + 16) = switch_endian64(data[2]);
*(uint64_t*)(ptr + 24) = switch_endian64(data[3]);
*(uint64_t*)(ptr + 32) = switch_endian64(data[4]);
*(uint64_t*)(ptr + 40) = switch_endian64(data[5]);
*(uint64_t*)(ptr + 48) = switch_endian64(data[6]);
*(uint64_t*)(ptr + 56) = switch_endian64(data[7]);
}
uint8_t ld8u ( uint64_t addr )
{
return *(uint8_t *)(get_ld_ptr(addr));
}
int8_t ld8s( uint64_t addr )
{
return *( int8_t *)(get_ld_ptr(addr));
}
uint16_t ld16u( uint64_t addr )
{
return switch_endian16(*(uint16_t*)(get_ld_ptr(addr)));
}
int16_t ld16s( uint64_t addr )
{
return switch_endian16(*( int16_t*)(get_ld_ptr(addr)));
}
uint32_t ld32u( uint64_t addr )
{
return switch_endian32(*(uint32_t*)(get_ld_ptr(addr)));
}
int32_t ld32s( uint64_t addr )
{
return switch_endian32(*( int32_t*)(get_ld_ptr(addr)));
}
uint64_t ld64( uint64_t addr )
{
return switch_endian64(*(uint64_t*)(get_ld_ptr(addr)));
}
void ld128( uint64_t addr, uint64_t data[2] )
{
uint8_t* ptr = get_ld_ptr(addr);
data[0] = switch_endian64(*(uint64_t*)(ptr + 0));
data[1] = switch_endian64(*(uint64_t*)(ptr + 8));
}
void ld512( uint64_t addr, uint64_t data[8] )
{
uint8_t* ptr = get_ld_ptr(addr & ~uint64_t(0x3f));
data[0] = switch_endian64(*(uint64_t*)(ptr + 0));
data[1] = switch_endian64(*(uint64_t*)(ptr + 8));
data[2] = switch_endian64(*(uint64_t*)(ptr + 16));
data[3] = switch_endian64(*(uint64_t*)(ptr + 24));
data[4] = switch_endian64(*(uint64_t*)(ptr + 32));
data[5] = switch_endian64(*(uint64_t*)(ptr + 40));
data[6] = switch_endian64(*(uint64_t*)(ptr + 48));
data[7] = switch_endian64(*(uint64_t*)(ptr + 56));
}
void ld256( uint64_t addr, uint64_t data[4] )
{
uint8_t* ptr = get_ld_ptr(addr & ~uint64_t(0x1f));
data[0] = switch_endian64(*(uint64_t*)(ptr + 0));
data[1] = switch_endian64(*(uint64_t*)(ptr + 8));
data[2] = switch_endian64(*(uint64_t*)(ptr + 16));
data[3] = switch_endian64(*(uint64_t*)(ptr + 24));
}
int block_read(uint64_t addr, uint8_t *tgt, int _size);
int block_write(uint64_t addr, const uint8_t *src, int _size);
int dump ( FILE * fp );
int restore ( FILE * fp );
// no mem page allocation on load accesses;
// if load goes to uninit location - return unknow value;
uint8_t* get_ld_ptr ( uint64_t addr )
{
uint8_t*** o1 = (uint8_t***)((char*)l1 + ((addr >> l1shft) & l1mask));
uint8_t** l2 = *o1;
if (l2 == 0)
return uninit_page + ( addr & 0x7 );
uint8_t** o2 = (uint8_t**)((char*)l2 + ((addr >> l2shft) & l2mask));
uint8_t* l3 = *o2;
if (l3 == 0)
return uninit_page + ( addr & 0x7 );
return l3 + (addr & l3mask);
}
// allocate mem page if store goes to uninit location;
// acquire a lock to prevent multiple writers on MP run;
uint8_t* get_st_ptr( uint64_t addr )
{
uint8_t*** o1 = (uint8_t***)((char*)l1 + ((addr >> l1shft) & l1mask) );
uint8_t** l2 = *o1;
if (l2 == 0)
{
mutex_lock(&l2_lock);
// check again if level 2 table is already allocated
l2 = *o1 ;
if(l2 == 0)
l2 = *o1 = (uint8_t**)calloc(l2size,sizeof(uint8_t));
mutex_unlock(&l2_lock);
}
uint8_t** o2 = (uint8_t**)((char*)l2 + ((addr >> l2shft) & l2mask));
uint8_t* l3 = *o2;
if (l3 == 0)
{
mutex_lock(&l3_lock);
// check again if level 3 page is already allocated
l3 = *o2;
if(l3 == 0)
l3 = *o2 = (uint8_t*)calloc(l3size,sizeof(uint8_t));
mutex_unlock ( &l3_lock );
}
return l3 + (addr & l3mask);
}
uint64_t get_l1size() { return l1size; }
uint64_t get_l2size() { return l2size; }
uint64_t get_l3size() { return l3size; }
private:
void lock ( uint64_t addr ) { mutex_lock ( &locks[(addr >> 4) & (SAM_NMEM_LOCKS -1)] ); }
void unlock ( uint64_t addr ) { mutex_unlock ( &locks[(addr >> 4) & (SAM_NMEM_LOCKS -1)] ); }
private:
uint8_t*** l1;
uint_t l1bits;
uint_t l2bits;
uint_t l3bits;
uint_t l1shft;
uint_t l2shft;
uint64_t l1size;
uint64_t l2size;
uint64_t l3size;
uint64_t l1mask;
uint64_t l2mask;
uint64_t l3mask;
uint64_t size;
uint8_t uninit_page[512];
mutex_t locks [SAM_NMEM_LOCKS ];
mutex_t l2_lock;
mutex_t l3_lock;
};
#endif //__IOMEM_SPARSE_MEM_H__