Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / devices / ioram / sparse_mem.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: sparse_mem.cc
// 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 ============================================
// this is a modular adaptation of sparse memory model in <ws>/mem.
// this approach allows for multiple sparse memory segments to co-exist
// within SAM run time image. It should even be possible to mix'n'match
// sparse segments with flat segments
#include "sparse_mem.h"
SparseMemory::SparseMemory
(
uint64_t ram_size,
uint_t _l1bits,
uint_t _l2bits,
uint_t _l3bits
)/*{{{*/
:
l1(0),
l1bits(_l1bits),
l2bits(_l2bits),
l3bits(_l3bits),
l1shft((l2bits + l3bits) - 3),// Compensate for pointer indexing, as I don't trust compiler
l2shft(l3bits - 3), // for doing a good job on the index computation code
l1size((1 << l1bits) << 3),
l2size((1 << l2bits) << 3),
l3size( 1 << l3bits),
l1mask(((1 << l1bits) - 1) << 3),
l2mask(((1 << l2bits) - 1) << 3),
l3mask(((1 << l3bits) - 1))
{
assert(l3bits >= 13);
size = ram_size;
memset(uninit_page,0,512);
l1 = (uint8_t***)calloc(l1size, sizeof(uint8_t));
for (int i = 0; i < SAM_NMEM_LOCKS; i++)
mutex_init(&locks[i], USYNC_THREAD, NULL);
mutex_init(&l2_lock, USYNC_THREAD, NULL);
mutex_init(&l3_lock, USYNC_THREAD, NULL);
}
SparseMemory::~SparseMemory()/*{{{*/
{
for (int i1=0; i1 < (1 << l1bits); i1++)
{
uint8_t** l2 = l1[i1];
if (l2)
{
for (int i2=0; i2 < (1 <<l2bits); i2++)
{
uint8_t* l3= l2[i2];
if (l3)
{
free(l3);
}
}
free(l2);
}
}
free(l1);
}
////////////////////////////////////////////////////////////////
//
// mem block copy
//
int SparseMemory::block_read(uint64_t addr, uint8_t *tgt, int _size)
{
//for (int i=0; i<_size; i++) tgt[i] = ld8u(addr+i);
while(_size)
{
uint8_t* paddr = get_st_ptr (addr); // page allocate
uint64_t offset = addr & l3mask;
uint64_t pagebytes = l3size - offset;
uint64_t nb = (_size > pagebytes) ? pagebytes : _size;
memcpy(tgt, paddr, (size_t)nb);
_size -= (int) nb;
addr += nb;
tgt += nb;
} // while more bytes to copy
return 0;
}
int SparseMemory::block_write(uint64_t addr, const uint8_t *src, int _size)
{
while(_size)
{
uint8_t* paddr = get_st_ptr (addr);
uint64_t offset = addr & l3mask;
uint64_t pagebytes = l3size - offset;
uint64_t nb = (_size > pagebytes) ? pagebytes : _size;
memcpy(paddr, src, (size_t)nb);
_size -= (int) nb;
addr += nb;
src += nb;
} // while more bytes to copy
return 0;
}
enum { BUFFER_SIZE = 512 };
static char buffer[BUFFER_SIZE];
int SparseMemory::load_img( const char* mem_image_filename, uint64_t start_pa )/*{{{*/
{
const char* separ = " \t\n";
FILE* image = fopen(mem_image_filename,"r");
uint64_t addr = 0;
if (!image)
{
fprintf(stderr,"No such mem image file found.");
return 1;
}
while (fgets(buffer,BUFFER_SIZE,image))
{
if (buffer[0] == '@')
{
addr = strtoull(buffer+1,0,16);
if(addr < start_pa || addr >= start_pa + size){
printf("invalid address %llx in %s\n",addr,mem_image_filename);
return 1;
}
addr -= start_pa;
}
else if (isxdigit(buffer[0]))
{
char* token = strtok(buffer,separ);
uint64_t length = strlen(token);
if (length == 16)
{
do
{
uint64_t data = strtoull(token,0,16);
if(addr >= size){
printf("invalid address %llx in %s\n",addr,mem_image_filename);
return 1;
}
st64_nl(addr,data);
addr += 8;
}
while ((token = strtok(0,separ)));
}
else if (length == 8)
{
do
{
uint32_t data = uint32_t(strtoul(token,0,16));
if(addr >= size){
printf("invalid address %llx in %s\n",addr,mem_image_filename);
return 1;
}
st32(addr,data);
addr += 4;
}
while ((token = strtok(0,separ)));
}
else if ((length == 1) && buffer[0] == '0')
{
// skip empty line
continue;
}
else
{
fprintf(stderr,"A memory entry must be either 4 or 8 bytes.");
return 1;
}
}
}
fclose(image);
return 0;
}
/*}}}*/
int SparseMemory::load_bin( const char *file, uint64_t addr)/*{{{*/
{
FILE *fp = fopen (file, "r");
struct stat s;
if (fp == NULL)
{
perror (file);
return 1;
}
if (stat(file, &s))
{
perror (file);
return 1;
}
uint64_t fsize = s.st_size;
if(addr + fsize > size){
printf("File %s size more than allocated memory\n");
return 1;
}
printf(" loading %s, relative base addr 0x%016llx, size 0x%llx\n", file, addr, fsize);
int i = 0;
uint64_t start_addr = addr;
while(fsize > 0)
{
uint8_t* paddr = get_st_ptr (addr);
uint64_t offset = addr & l3mask;
uint64_t pagebytes = l3size - offset;
uint64_t nb = (fsize > pagebytes) ? pagebytes : fsize;
nb = fread(paddr, sizeof(uint8_t), nb, fp);
fsize -= nb;
addr += nb;
// display the progress
printf (".");
fflush(NULL);
if (++i % 50 == 0)
printf ("%lld bytes \n", addr - start_addr);
} // while more bytes to copy
return 0;
}
int SparseMemory::load_elf(const char *filename, uint64_t base_pa)
{
int fd;
Elf *elf;
Elf64_Ehdr *ehdr;
Elf_Scn *scn;
Elf64_Shdr *shdr;
Elf_Data *data;
u_int cnt;
uint64_t base_va;
/* Open the input file */
fd = open(filename, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "load_elf <%s> not found \n", filename);
exit (0);
}
/* Obtain the ELF descriptor */
(void) elf_version(EV_CURRENT);
if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
fprintf(stderr, "%s\n", elf_errmsg(elf_errno()));
exit(0);
}
/* Obtain the .shstrtab data buffer */
if (((ehdr = elf64_getehdr(elf)) == NULL) ||
((scn = elf_getscn(elf, ehdr->e_shstrndx)) == NULL) ||
((data = elf_getdata(scn, NULL)) == NULL)) {
fprintf(stderr, "%s\n", elf_errmsg(elf_errno()));
exit(0);
}
base_va = ehdr->e_entry;
/* Traverse input filename, looking for relevant sections */
for (cnt = 1, scn = NULL; scn = elf_nextscn(elf, scn); cnt++) {
if ((shdr = elf64_getshdr(scn)) == NULL) {
fprintf(stderr, "%s\n", elf_errmsg(elf_errno()));
exit(0);
}
if (strcmp((char *)data->d_buf+shdr->sh_name,
".text") == 0) {
load_elf64_section(shdr, scn, base_pa, base_va);
} else if (strcmp((char *)data->d_buf+shdr->sh_name,
".rodata") == 0) {
load_elf64_section(shdr, scn, base_pa, base_va);
} else if (strcmp((char *)data->d_buf+shdr->sh_name,
".rodata1") == 0) {
load_elf64_section(shdr, scn, base_pa, base_va);
} else if (strcmp((char *)data->d_buf+shdr->sh_name,
".data") == 0) {
load_elf64_section(shdr, scn, base_pa, base_va);
} else if (strcmp((char *)data->d_buf+shdr->sh_name,
".data1") == 0) {
load_elf64_section(shdr, scn, base_pa, base_va);
}
}
elf_end(elf);
close(fd);
printf("elf_load file %s complete\n",filename);
return 0;
}
void SparseMemory::load_elf64_section(Elf64_Shdr *shdr, Elf_Scn *scn,
uint64_t base_pa, uint64_t base_va)
{
uint64_t offset;
Elf_Data *data;
if ((data = elf_getdata(scn, NULL)) == NULL) {
fprintf(stderr, "%s\n", elf_errmsg(elf_errno()));
exit(0);
}
offset = shdr->sh_addr - base_va;
uint8_t * d = (uint8_t*)(data->d_buf);
for(int i = 0; i < data->d_size ; i++)
st8(base_pa + offset + i, d[i]);
}
// the difference between save and dump is that save writes every byte into the
// file 'filename' where as dump writes only dirty bytes into 'filename'. Hence
// dump is easy on disk space. save can be used either as a debugging aid or to
// modify and save (eg nvram.bin) loaded files. dump is intended to be used in
// SAM dump/restore operation.
int SparseMemory::save( const char* filename, uint64_t addr, uint64_t _size )/*{{{*/
{
const int PAGE_SIZE = 1 << 13;
const int PAGE_MASK = PAGE_SIZE - 1;
FILE* image = fopen(filename,"w");
if (!image)
{
perror (filename);
return 0;
}
if(size == 0){
fclose(image);
return 1;
}
uint8_t *buf8 = (uint8_t *)malloc(PAGE_SIZE);
if ((addr & PAGE_MASK) != 0)
{
uint64_t n = PAGE_SIZE - (addr & PAGE_MASK);
if (_size < n)
n = _size;
block_read(addr, buf8, int(n));
fwrite(buf8,1,n,image);
addr += n;
_size -= n;
}
while (_size >= PAGE_SIZE)
{
block_read(addr, buf8, PAGE_SIZE);
fwrite(buf8,1,PAGE_SIZE,image);
addr += PAGE_SIZE;
_size -= PAGE_SIZE;
}
if (_size)
{
block_read(addr, buf8, _size);
fwrite(buf8,1,_size,image);
}
free(buf8);
fclose(image);
return 1;
}
#define DUMPSIZE 1024
#define DISPLAY_SIZE (0x20000000LLU) // 512M
#define THRESHOLD_512M (0x20000000LLU)
#define THRESHOLD_4G (0x100000000LLU)
#define THRESHOLD_16G (0x400000000LLU)
#define MASK_32M (0x1ffffffLLU)
#define MASK_128M (0x7ffffffLLU)
#define MASK_1G (0x3fffffffLLU)
#define MASK_4G (0xffffffffLLU)
extern void dump_uint32 (FILE * fp, uint32_t val);
extern void dump_uint64 (FILE * fp, uint64_t val);
extern uint64_t restore_uint64 (FILE * fp);
int SparseMemory::dump ( FILE * fp )
{
// dump page sizes
dump_uint64(fp, l1size);
dump_uint64(fp, l2size);
dump_uint64(fp, l3size);
uint64_t npages = 0;
uint64_t dpages = 0;
for (uint64_t i1=0; i1 < (1 << l1bits); i1++)
{
uint8_t** l2 = l1[i1];
if (l2)
{
for (uint64_t i2=0; i2 < (1 <<l2bits); i2++)
{
uint64_t page_addr = (i1 << (l2bits + l3bits)) | (i2 << l3bits);
uint8_t* l3= l2[i2];
if (l3)
{
// start page dump
fprintf (stderr,".");
// page address
dump_uint64(fp, page_addr);
// dump mem page
if (fwrite ( (char*)l3, l3size, 1, fp ) != 1)
{
printf("dump %s",name);
perror ("");
return 0;
}
dpages++; // number of dirty pages
}
npages++; // number of pages
}
}
}
// last page delimiter
dump_uint64(fp,0xFFFFFFFFFFFFFFFFLLU);
fclose (fp);
fprintf (stderr, "%s: dumped 0x%llx (modified 0x%llx) pages \n", name, npages, dpages);
return 1;
}
SparseMemory::restore ( FILE * fp)
{
uint64_t r_l1size = restore_uint64(fp);
uint64_t r_l2size = restore_uint64(fp);
uint64_t r_l3size = restore_uint64(fp);
// check mem configuration
if (r_l1size != get_l1size() ||
r_l2size != get_l2size() ||
r_l3size != get_l3size() )
{
fprintf(stderr, "%s: restore : sparse mem configuration does not match\n",name);
return 0;
}
uint64_t zpages = 0;
while (1)
{
uint64_t page_addr = restore_uint64(fp);
if (page_addr == 0xFFFFFFFFFFFFFFFFLLU)
break; // last page was restored
// allocate a new mem page
uint8_t* mem_page = get_st_ptr( page_addr );
// restore page
if (fread(mem_page, r_l3size, 1, fp) != 1)
{
fprintf(stderr, "%s: restore : restore state failed\n",name);
return 0;
}
zpages++;
}
fprintf (stderr, "%s: restored 0x%llx pages \n", name, zpages);
return 1;
}