Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / system / mem / Memory.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: Memory.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 ============================================
/*
* Copyright (C) 2005 Sun Microsystems, Inc.
* All rights reserved.
*/
#include <errno.h>
#include <signal.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include "types.h"
#include "dr.h"
#include "ui.h"
#include "system.h"
#include "Memory.h"
char mem_help_string[] = "Usage : mem -a <start addr> -s <num_of_bytes> [-cpu <id>][-dis -va]\n";
char memdump_help_string[] = "memdump <filename> <start phys addr> <size>\n";
static int mem_cmd_action (void * , int argc, char **argv);
static int memdump_cmd_action (void * , int argc, char **argv);
bool_t mem_dump (DR_OPAQUE pdr);
bool_t mem_restore (DR_OPAQUE pdr);
#if defined(MEMORY_FLAT)
// constructor , e.g. SMemory(uint64_t(0x1) << 36,48);
SMemory::SMemory
(
uint64_t _ram_size, // mem size (bytes)
uint_t pa_bits // number of valid bits in physical address
)
:
mem(0), size(_ram_size)
{
assert(pa_bits < 64);
pa_mask = (uint64_t(1)<<pa_bits)-1;
for (int i = 0; i < SAM_NMEM_LOCKS; i++)
mutex_init(&locks[i], USYNC_THREAD, NULL);
DR_register ((char*)"mm1", mem_dump, mem_restore, (void*)this);
UI_register_cmd_2 ((char*)"mem", mem_help_string, mem_cmd_action, NULL);
UI_register_cmd_2 ((char*)"memdump", memdump_help_string, memdump_cmd_action, NULL);
}
// mem init method
int SMemory::init
(
char *file_name, // file name for private mem file, default is NULL
int is_private // 1 if private file, 0 if a temporary file (default)
)
{
// check if init method was called second time
if (mem && (mem != MAP_FAILED))
{
munmap((char*)mem,size);
}
int prot = PROT_READ|PROT_WRITE;
int flags = MAP_ALIGN;
mfile = -1;
if (file_name == NULL)
{
// map anonymous fragment (in swap space);
// for large mem size it requires to have enough
// swap space on the host machine
flags |= MAP_ANON;
if ( !SYSTEM_get_memreserve() )
{
// do not reserve swap space before the run
ui->warning("You might run out of swap space with -nomemreserve option \n");
flags |= MAP_NORESERVE|MAP_PRIVATE;
}
else
{
// swap space should be reserved before the run
flags |= MAP_SHARED;
}
mem = (uint8_t*)mmap((char*)0,size, prot, flags, -1, 0);
}
else // map memory image to a file
{
if (file_name[0] == '/')
{
// this is an absolute path
mem_file = strdup(file_name);
}
else // file in the working directory
{
char path[PATH_MAX];
if (getwd(path) == NULL)
{
ui->error("memory: cannot get current working directory\n");
return 0;
}
mem_file = (char *)calloc(strlen(path)+strlen(file_name)+16, sizeof(char));
sprintf(mem_file,"%s/%s", path, file_name);
}
ui->output("map ram image to %s, size = %lld bytes\n", mem_file, size);
if (is_private)
{
// open a checkpoint mem image
int img = open ( mem_file, O_RDONLY|O_LARGEFILE );
if (img < 0)
{
ui->error("memory: cannot open %s, %s\n",mem_file, strerror(errno));
return 0;
}
// To avoid the mem image file to be modified during the run
// map it as a private - all changes will go to pages allocated in
// swap space
if ( !SYSTEM_get_memreserve() )
{
// do not reserve swap space before the run
ui->warning("You might run out of swap space with -nomemreserve option \n");
flags |= MAP_NORESERVE;
}
mem = (uint8_t*)mmap((char*)0,size, prot, flags|MAP_PRIVATE, img, 0);
}
else // temporary mem file
{
if (access(mem_file, W_OK)==0)
{
ui->error("memory: another sim is already using %s file\n",mem_file);
return 0;
}
// open a new mem file
mfile = open (mem_file, O_RDWR|O_CREAT|O_LARGEFILE|O_TRUNC,
S_IRWXO|S_IRWXG|S_IRWXU);
if (mfile < 0)
{
ui->error("memory: cannot open %s, %s\n", mem_file, strerror(errno));
return 0;
}
// write the very last byte in the mem file
// to set the effective size of the file
if (lseek(mfile, size-1, SEEK_SET) < 0)
{
ui->error("memory: cannot set mem file size, %s \n", strerror(errno));
return 0;
}
if (write(mfile, "", 1) != 1)
{
ui->error("memory: cannot write mem file, %s \n", strerror(errno));
return 0;
}
mem = (uint8_t*)mmap((char*)0,size, prot, flags|MAP_SHARED, mfile, 0);
}
} // map to a file
if (mem == MAP_FAILED)
{
ui->error("memory: cannot mmap file, %s\n",strerror(errno));
return 0;
}
return 1;
}
/*}}}*/
// load bin format
int SMemory::load_bin( const char *file, uint64_t addr)/*{{{*/
{
FILE *fp = fopen (file, "r");
struct stat s;
if (fp == NULL)
{
ui->perror(file);
return 1;
}
if (stat(file, &s))
{
ui->perror(file);
return 1;
}
uint64_t fsize = s.st_size;
ui->verbose("loading %s, base addr 0x%016llx, size 0x%llx\n", file, addr, fsize);
// check if bin file could fit into the allocated memory
if (addr+fsize > get_size())
{
ui->error("MEM: ram_size = 0x%llx, cannot load 0x%llx bytes \n", get_size(), fsize);
return 1;
}
uint8_t *start_addr = (uint8_t *)(mem + ofs(addr));
if(fread(start_addr, sizeof(uint8_t), fsize, fp) == 0)
{
ui->error("MEM: cannot read file %s \n", file);
return 1;
}
fclose(fp);
return 0;
}
SMemory::~SMemory()/*{{{*/
{
if (mem && (mem != MAP_FAILED))
{
munmap((char*)mem,size);
}
if (mfile > 0)
{
close ( mfile );
remove( mem_file );
}
if (mem_file)
{
free ( mem_file );
}
}
/*}}}*/
///////////////////////////////////////////////////////////////
void SMemory::handle_out_of_range ( uint64_t addr )
{
ui->error("memory: address %llx exceeds mem range %llx, redirect access to address 0; bus error was not signaled \n", addr, size);
exit(1);
}
#elif defined(MEMORY_SPARSE)
SMemory::SMemory
(
uint64_t ram_size,
uint_t pa_bits,
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;
pa_mask = (uint64_t(1)<<pa_bits)-1;
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);
mlist = 0;
DR_register ((char*)"mm1", mem_dump, mem_restore, (void*)this);
UI_register_cmd_2 ((char*)"mem", mem_help_string, mem_cmd_action, NULL);
UI_register_cmd_2 ((char*)"memdump", memdump_help_string, memdump_cmd_action, NULL);
}
SMemory::~SMemory()/*{{{*/
{
// memory object is constructed only once and
// cannot be reconfigured during the run;
// rely on the system to free up all allocated memory on exit();
return;
/* normally we would need to
* remove sparse mem tables
*/
#if 0
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)
{
// it maybe mmaped to a file;
// if some random value is passed to free(),
// the results are undefined.
free(l3);
}
}
free(l2);
}
}
free(l1);
// remove mmaped files
while(mlist)
{
MappedFileEntry *next = mlist->next;
delete(mlist);
mlist = next;
}
#endif
}
////////////////////////////////////////////////////////////////
//
// mem block copy
//
int SMemory::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 SMemory::block_write(uint64_t addr, const uint8_t *src, int _size)
{
//for (int i=0; i<_size; i++) st8(addr+i, src[i]);
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;
}
// construct memory mapped file entry
MappedFileEntry::MappedFileEntry
(
const char *file_name, // name of the file
uint64_t saddr // starting address
)
:
name (0),
mfile(-1),
mem (0),
size (0),
next (0)
{
addr = saddr; // keep starting address
// check file name
if (file_name[0] == '/')
{
// this is an absolute path
name = strdup(file_name);
}
else // file is in working directory
{
char path[PATH_MAX];
if (getwd(path) == NULL)
{
ui->error("memory: cannot get current working directory\n");
return;
}
// keep absolute file name
name = (char *)calloc(strlen(path)+strlen(file_name)+16, sizeof(char));
sprintf(name,"%s/%s", path, file_name);
}
// check file size
struct stat s;
if (stat(name, &s))
{
ui->perror(name);
return;
}
size = s.st_size;
ui->verbose("loading %s, base addr 0x%016llx, size 0x%llx\n", name, addr, size);
// open the file
mfile = open ( name, O_RDONLY|O_LARGEFILE );
if (mfile < 0)
{
ui->error("memory: cannot open %s, %s\n",name, strerror(errno));
return;
}
// map file as a private - all changes will go to pages allocated in swap space
int prot = PROT_READ|PROT_WRITE;
int flags = MAP_ALIGN|MAP_PRIVATE;
if ( !SYSTEM_get_memreserve() )
{
// do not reserve swap space before the run
ui->warning("You might run out of swap space with -nomemreserve option \n");
flags |= MAP_NORESERVE;
}
mem = (uint8_t*)mmap((char*)0,size, prot, flags, mfile, 0);
}
MappedFileEntry::~MappedFileEntry()
{
if (mem && (mem != MAP_FAILED))
{
munmap((char*)mem,size);
}
if (mfile > 0)
{
close ( mfile );
}
if (name)
{
free (name);
}
}
// load bin format
int SMemory::load_bin( const char *file, uint64_t addr)/*{{{*/
{
MappedFileEntry *mm_entry = new MappedFileEntry(file, addr);
if (!mm_entry->is_valid() || !this->map(mm_entry))
{
ui->error("cannot load %s, base addr 0x%016llx\n", file, addr);
delete mm_entry;
return 1;
}
link(mm_entry);
return 0;
}
// map page from the mmaped file
int SMemory::map_page
(
uint64_t paddr, // page address
uint8_t *maddr // page address in the mmapped file
)
{
// look up the entry in the tables where paddr suppose to reside
uint8_t*** o1 = (uint8_t***)((char*)l1 + ((paddr >> l1shft) & l1mask) );
uint8_t** l2 = *o1;
if (l2 == 0)
{
l2 = *o1 = (uint8_t**)calloc(l2size,sizeof(uint8_t));
}
if (l2==0)
{
ui->error("\nMEM: Run out of memory, exit...\n");
exit(1);
}
uint8_t** o2 = (uint8_t**)((char*)l2 + ((paddr >> l2shft) & l2mask));
uint8_t* l3 = *o2;
if (l3 == 0) // map the page
{
*o2 = maddr;
}
else // page is already allocated
{
return 0;
}
return 1;
}
// map sparse mem pages to mem mapped file
int SMemory::map
(
MappedFileEntry *entry
)
{
// init counters and pointers
int npages = 0; // page counter
uint64_t start_addr = entry->addr;
uint64_t addr = start_addr;
uint64_t fsize = entry->size;
uint8_t *faddr = entry->mem;
off_t foff = 0;
while ( fsize > 0 )
{
uint64_t offset = addr & l3mask;
uint64_t pagebytes = l3size - offset;
// number of bytes that should be on the same sparse mem page
uint64_t nb = (fsize > pagebytes) ? pagebytes : fsize;
// if this address range doesn't fit exactly to the sparse mem page
// and can't be memory mapped (because a previous file as been
// mapped to this range), just allocate space with get_st_ptr and
// read from the file.
if (nb != l3size || !map_page(addr, faddr))
{
// make a copy of this page
uint8_t* paddr = get_st_ptr (addr);
// seek to the beginning of the "page" to copy
if (lseek(entry->mfile, foff, SEEK_SET) == -1)
{
ui->error("\nMEM: can't seek to 0x%llx for file %s\n",
foff, entry->name);
exit(1);
}
if (read(entry->mfile, paddr, nb) != nb)
{
ui->error("\nMEM: can't read 0x%llx bytes from file %s\n",
nb, entry->name);
exit(1);
}
}
fsize -= nb;
addr += nb;
faddr += nb;
foff += nb;
} // while something left in the file
return 1;
}
#endif // end MEMORY_SPARSE
enum { BUFFER_SIZE = 512 };
static char buffer[BUFFER_SIZE];
// load image format;
// use st64_nl, common for all mem models
int SMemory::load( const char* mem_image_filename )/*{{{*/
{
const char* separ = " \t\n";
FILE* image = fopen(mem_image_filename,"r");
uint64_t addr = 0;
if (!image)
{
ui->error("MEM: No such mem image file found.");
return 1;
}
while (fgets(buffer,BUFFER_SIZE,image))
{
if (buffer[0] == '@')
{
addr = strtoull(buffer+1,0,16);
}
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);
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));
st32(addr,data);
addr += 4;
}
while ((token = strtok(0,separ)));
}
else if ((length == 1) && buffer[0] == '0')
{
// skip empty line
continue;
}
else
{
ui->error("A memory entry must be either 4 or 8 bytes.");
return 1;
}
}
}
fclose(image);
return 0;
}
/*}}}*/
void SMemory::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)
{
ui->perror(filename);
return;
}
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);
}
//////////////////////////////////////////////////
///
/// Memory object DUMP/RESTORE
///
//////////////////////////////////////////////////
#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)
#define MAX_ADDR (0xFFFFFFFFFFFFFFFFLLU)
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);
////////////////////////////////////////////////////////
//
// restore mem from dump file
//
int SMemory::restore ( char *dir )
{
if (DR_restore_object (dir, (char*)"mm1"))
return 1;
return 0;
}
bool_t mem_dump (DR_OPAQUE pdr)
{
char *name = DR_get_name (pdr);
char *dir = DR_get_dir ();
SMemory *msp = (SMemory*) DR_get_client_data (pdr);
return msp->dump(dir, name);
}
#if defined(MEMORY_FLAT)
///////////////////////////////////////////////////
int SMemory::dump ( char *dir, char *name)
{
FILE *fp;
uint64_t i,j;
uint64_t npages = this->get_size()/DUMPSIZE;
uint64_t mask;
uint64_t cur_addr = 0;
uint64_t dpages = 0;
char fname[PATH_MAX];
sprintf (fname, "%s/%s.dmp.img", dir, name);
// set to false if older mm1.dmp (v4) format is needed
bool img_format = true;
if ((fp = fopen (fname, "w")) == NULL)
{
ui->perror(fname);
return 0;
}
// depending on memsize mask defines progress update frequency
if (this->get_size() <= THRESHOLD_512M) {
mask = MASK_32M;
}
else if (this->get_size() <= THRESHOLD_4G) {
mask = MASK_128M;
}
else if (this->get_size() <= THRESHOLD_16G) {
mask = MASK_1G;
}
else {
mask = MASK_4G;
}
ui->output("MEM: ram_size = 0x%llx (%lld MB), pages = 0x%lld\n",
get_size(), get_size()>>20, npages);
for (i = 0; i < npages; i++)
{
bool_t clean = 1;
cur_addr += DUMPSIZE;
// show the progress
if ((cur_addr & mask) == 0)
{
ui->output(".");
ui->flush();
}
uint64_t paddr = i*DUMPSIZE;
uint8_t *mem_page = get_base() + paddr;
// check if page is modified - has non-zero values;
// sparse mem files always read 0 from address that
// was never written, by default;
for (j = 0; j < DUMPSIZE; j = j + 8)
{
if (*(uint64_t *) (mem_page + j) != 0) {
clean = 0;
break;
}
}
if (!clean) // dump modified page
{
if (img_format)
{
// write the page using sparse file
if ( fseek(fp, paddr, SEEK_SET) < 0 )
{
ui->error("MEM: cannot set page addr 0x%llx, %s \n",
paddr, strerror(errno));
return 0;
}
}
else // old dmp format
{
dump_uint64(fp, i); // page number
}
if ( fwrite (mem_page, DUMPSIZE, 1, fp) != 1 )
{
ui->perror("DUMP (mm1)");
return 0;
}
dpages++;
}
}
if (img_format)
{
// make sure that the very last byte is written
// to set correct file size
uint64_t last_byte = get_size() - 1;
fseek(fp, last_byte, SEEK_SET);
uint8_t *plast_byte = get_base() + last_byte;
if ( fwrite (plast_byte, 1, 1, fp) != 1 )
{
ui->perror("DUMP (mm1)");
return 0;
}
}
else
{
i = MAX_ADDR;
dump_uint64(fp, i);
}
fclose (fp);
ui->output("MEM: dumped 0x%llx (modified 0x%llx) pages \n", npages, dpages);
return 1;
} // mem_dump()
/////////////////////////////////////////////
//
// restore command
//
bool_t mem_restore (DR_OPAQUE pdr)
{
char *name = DR_get_name (pdr);
char *dir = DR_get_rdir (pdr);
SMemory *msp = (SMemory*) DR_get_client_data (pdr);
FILE *fp;
uint64_t npages = msp->get_size()/DUMPSIZE;
uint64_t zpages = 0;
uint64_t page_no;
char *mem;
char fname[PATH_MAX];
sprintf (fname, "%s/%s.dmp.img", dir, name);
if ( access(fname, R_OK) ==0 )
{
// restore mem image checkpoint;
// map it as a private file
if (msp->init(fname, 1))
{
ui->output("MEM: restored 0x%llx bytes \n", msp->get_size());
return 1;
}
return 0;
}
// restore from mm1.dmp file
if( !msp->init() )
{
ui->error("MEM: cannot allocate enough space \n");
return 0;
}
// mem dmp checkpoint
sprintf (fname, "%s/%s.dmp", dir, name);
if ((fp = fopen (fname, "r")) == NULL) {
ui->perror(fname);
return 0;
}
fseek(fp, 0, SEEK_END);
size_t fsize = ftell(fp);
fseek(fp, 0, SEEK_SET);
int ztotal = (fsize/DUMPSIZE);
ui->output("(dump size %lld MB):\n", (uint64_t)(fsize>>20));;
int pcttarget = 0;
// copy non-zero pages to mem file
while (1) {
uint64_t offset;
page_no = restore_uint64(fp);
if (page_no == MAX_ADDR) {
break;
}
offset = page_no*DUMPSIZE;
if (offset > (msp->get_size() - DUMPSIZE)) {
ui->output("MEM: restore to 0x%llx is beyond memory ram_size (0x%llx), discarding...\n",
offset, msp->get_size());
return 0;
}
else {
mem = (char*) (msp->get_base()+offset);
}
if (fread(mem, DUMPSIZE, 1, fp) != 1) {
ui->output("MEM: restore : restore state failed\n");
return 0;
}
zpages++;
int pct = (100.0*zpages/ztotal);
if (pct >= pcttarget) {
ui->output("%d%% ", pct);
ui->flush();
pcttarget = pct+2;
}
}
ui->output("MEM: restored 0x%llx pages \n", zpages);
return 1;
}
#elif defined(MEMORY_SPARSE)
// dump sparse mem pages
int SMemory::dump ( char *dir, char* name)
{
FILE *fp, *fidx;
char fname[PATH_MAX], iname[PATH_MAX];
sprintf (fname, "%s/%s.dmp.img", dir, name);
sprintf (iname, "%s/%s.dmp.idx", dir, name);
ui->output("\n");
if ((fp = fopen (fname, "w")) == NULL)
{
ui->perror(fname);
return 0;
}
if ((fidx = fopen (iname, "w")) == NULL)
{
ui->perror(iname);
return 0;
}
// version number
dump_uint64(fidx, SAM_MEM_DUMP_VERSION);
// dump sparse mem table sizes
dump_uint64(fidx, l1size);
dump_uint64(fidx, l2size);
dump_uint64(fidx, l3size);
uint64_t npages = 0;
uint64_t dpages = 0;
uint64_t foffset = 0;
uint64_t next_page_addr = MAX_ADDR;
uint64_t block_addr = 0;
uint64_t block_offs = 0;
uint64_t block_size = l3size;
uint64_t nblocks = 0;
uint64_t nidx = 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) // dump this page
{
if ( page_addr == next_page_addr )
{
// it is a next page in the block
block_size += l3size;
}
else // start a new block
{
if (next_page_addr != MAX_ADDR)
{
// current block address,offset, size
dump_uint64(fidx, block_addr);
dump_uint64(fidx, block_offs);
dump_uint64(fidx, block_size);
nidx++;
}
// start a new block
block_addr = page_addr;
block_offs = foffset;
block_size = l3size;
next_page_addr = page_addr;
nblocks++;
}
if (is_dirty(l3)) dpages++;
// clear dirty bit
l3 = mask_dirty(l3);
l2[i2] = l3;
// dump mem page
if (fwrite ( (char*)l3, l3size, 1, fp ) != 1)
{
ui->perror("DUMP (mm1)");
return 0;
}
foffset += l3size;
next_page_addr += l3size;
npages++; // number of pages
// display progress
if (npages % 32 == 0) {
// erase previous value using back-space chars
ui->output("%-5d MB\r",(npages << (l3bits-20)) );
}
} // if (l3)
}
}
}
// last page
if (nblocks != nidx)
{
dump_uint64(fidx, block_addr);
dump_uint64(fidx, block_offs);
dump_uint64(fidx, block_size);
}
// delimiter
dump_uint64(fidx,MAX_ADDR);
dump_uint64(fidx,nblocks);
dump_uint64(fidx,npages);
fclose (fp);
fclose (fidx);
ui->output("\nMEM: dumped %lld pages (%lld dirty), %lld blocks \n", npages, dpages, nblocks);
return 1;
}
// restore from a checkpoint which has
// addresses and data are in one file
uint64_t restore_mem_checkpoint
(
SMemory *msp,
MappedFileEntry *entry
)
{
// we are here because we didn't find an index file
// if this is a blaze dump, it has to be a flat-memory dump
// otherwise, it has to be a vonk sparse memory dump with addresses & data in one file
// find the first vcpu and get its vcpu type
int cpuid;
Vcpu * first_vcpu = NULL;
for (cpuid=0; cpuid<=g_vcpu_id_max; cpuid++) {
first_vcpu = get_vcpu(cpuid);
if (first_vcpu != NULL) break;
}
if ((first_vcpu->config.cpu_type & VCPU_IMPL_SIM_MASK) == VCPU_IMPL_SIM_BLAZE ) {
ui->output("MEM: mapping in blaze flat-mem dump into sparse-memory model");
msp->map(entry);
return 0;
}
// read sparse mem table sizes
uint64_t r_l1size; read(entry->mfile, &r_l1size, 8);
uint64_t r_l2size; read(entry->mfile, &r_l2size, 8);
uint64_t r_l3size; read(entry->mfile, &r_l3size, 8);
// restored page counter
uint64_t zpages = 0;
// check mem configuration
if (r_l1size != msp->get_l1size() ||
r_l2size != msp->get_l2size() ||
r_l3size != msp->get_l3size() )
{
ui->output("MEM: restore : sparse mem configuration does not match\n");
return 0;
}
// add three table sizes and one page address 8 bytes each = 32
uint8_t *faddr = entry->mem + 32;
while (1)
{
// read page address
uint64_t page_addr; read(entry->mfile, &page_addr, 8);
if (page_addr == MAX_ADDR)
break; // last page was restored
// only l3size pages are written to the file
if (msp->map_page(page_addr, faddr))
{
// page is mapped to the mem file, adjust the file pointer
if (lseek(entry->mfile, r_l3size, SEEK_CUR) < 0)
{
ui->error("MEM: cannot set file pointer, %s \n", strerror(errno));
return 0;
}
}
else
{
// allocate a new mem page
uint8_t* mem_page = msp->get_st_ptr( page_addr );
// read the page from the file
uint64_t restore = read(entry->mfile, mem_page, r_l3size);
if (restore != r_l3size)
{
ui->output("MEM: read 0x%llx bytes from 0x%llx block : restore failed\n", restore, r_l3size);
return 0;
}
}
faddr += (r_l3size+8); // adjust for page address 8 bytes
zpages++;
}
return zpages;
}
// restore mem from the file;
// mem pages are set to point to mmaped file;
// use lseek() to find page addresses;
// speed of mem restore could be improved if addresses are
// stored in a serate file
bool_t mem_restore (DR_OPAQUE pdr)
{
char *name = DR_get_name (pdr);
char *dir = DR_get_rdir (pdr);
char fname[PATH_MAX];
char iname[PATH_MAX];
char dmpfname[PATH_MAX];
struct stat statbuf;
sprintf (fname, "%s/%s.dmp.img", dir, name);
sprintf (iname, "%s/%s.dmp.idx", dir, name);
SMemory *msp = (SMemory*) DR_get_client_data (pdr);
MappedFileEntry *mm_entry = new MappedFileEntry(fname);
if (!mm_entry->is_valid())
{
ui->error("mem_restore: cannot load ");
ui->perror(fname);
if (errno == ENOENT) {
// check if a blaze-v4 style mm1.dmp file exists
sprintf(dmpfname, "%s/%s.dmp", dir, name);
if (stat(dmpfname, &statbuf) == 0) {
ui->warning(
"This dump contains an old, unsupported version of the\n"
"memory dump file (%s). Please contact the owner of this\n"
"checkpoint to update this file.\n\n"
"If you are the owner, you can update this file using:\n\n"
" memdump2image -i %s -o %s\n\n",
dmpfname, dmpfname, fname);
} // if mm1.dmp exists
}
delete mm_entry;
return 0;
}
// add the file to mmapped list
msp->link(mm_entry);
// restored page counter
uint64_t zpages = 0;
uint64_t nblocks = 0;
// open index file
int fidx = open ( iname, O_RDONLY|O_LARGEFILE );
if (fidx < 0)
{
ui->error("MEM: cannot open %s, this is an older checkpoint version.\n",iname);
zpages = restore_mem_checkpoint(msp, mm_entry);
}
else
{
// read sparse mem table sizes
uint64_t r_ver; read(fidx, &r_ver, 8);
uint64_t r_l1size; read(fidx, &r_l1size, 8);
uint64_t r_l2size; read(fidx, &r_l2size, 8);
uint64_t r_l3size; read(fidx, &r_l3size, 8);
// check mem configuration
if (r_l1size != msp->get_l1size() ||
r_l2size != msp->get_l2size() ||
r_l3size != msp->get_l3size() )
{
ui->error("MEM: restore : sparse mem configuration does not match\n");
return 0;
}
while (1)
{
// read page address, size, offset in the file
uint64_t block_addr; read(fidx, &block_addr, 8);
uint64_t block_offs; read(fidx, &block_offs, 8);
uint64_t block_size; read(fidx, &block_size, 8);
if (block_addr == MAX_ADDR)
{
// check counters
if ( nblocks != block_offs || zpages != block_size )
{
ui->error("MEM: read 0x%llx blocks, 0x%llx pages - there should be 0x%llx blocks, 0x%llx pages : restore failed\n",
nblocks, zpages, block_offs, block_size);
return 0;
}
break; // last page was restored
}
nblocks++;
uint64_t page_addr = block_addr;
uint64_t page_offs = block_offs;
while(block_size>0)
{
// try to map page to the file
if ( ! msp->map_page(page_addr, mm_entry->mem + page_offs) )
{
// cannot mmap, something is already there - need to do a copy
// get mem page pointer
uint8_t* mem_page = msp->get_st_ptr( page_addr );
// adjust the file pointer
if (lseek(mm_entry->mfile, page_offs, SEEK_SET) < 0)
{
ui->error("MEM: cannot set file pointer, %s \n", strerror(errno));
return 0;
}
// read the page from the file
uint64_t restore = read(mm_entry->mfile, mem_page, r_l3size);
if (restore != r_l3size)
{
ui->error("MEM: read 0x%llx bytes from 0x%llx block : restore failed\n", restore, r_l3size);
return 0;
}
}
page_addr += r_l3size;
page_offs += r_l3size;
block_size -= r_l3size;
zpages++;
}
}
close(fidx);
}
ui->output("MEM: restored 0x%llx pages, 0x%llx blocks \n", zpages, nblocks );
return 1;
}
#endif // MEMORY_SPARSE
/////////////////////////////////////////////
//
// ui mem command
//
#include "cpu_interface.h"
extern SMemory *mm1;
void print_mem_cmd_usage()
{
ui->output("%s", mem_help_string);
}
static int mem_cmd_action (void *, int argc, char **argv)
{
uint64_t saddr=0;
uint64_t eaddr=0;
uint64_t size=16;
int i;
int is_va = 0;
int is_dis = 0;
int cpuid = 0;
if (mm1 == NULL)
{
ui->output("MEM: RAM is not allocated yet \n");
return 0;
}
if (argc < 2)
{
print_mem_cmd_usage();
return 1;
}
for (i=1; i<argc; i++)
{
if (strcmp(argv[i], "-dis")==0)
{
is_dis = 1;
ui->output( " disassemble");
continue;
}
else if (strcmp(argv[i], "-va" )==0)
{
is_va = 1;
ui->output( " addr is virtual");
continue;
}
else if ((strcmp(argv[i], "-cpu")==0) && ((i+1)<argc))
{
cpuid = int(strtol(argv[++i], NULL, 0));
Vcpu *vcpu = get_vcpu(cpuid);
if (!vcpu)
{
ui->output( "cpu id %i is unknown \n",cpuid);
return 0;
}
ui->output( " for cpu[%i]",cpuid);
continue;
}
else if ((strcmp(argv[i], "-a")==0) && ((i+1)<argc))
{
saddr = strtoull(argv[++i], NULL, 0);
saddr &= ~7LLU;
eaddr = saddr + 16;
continue;
}
else if ((strcmp(argv[i], "-s")==0) && ((i+1)<argc))
{
size = strtoull(argv[++i], NULL, 0);
continue;
}
else if (strcmp(argv[i], "?" )==0)
{
print_mem_cmd_usage();
return 1;
}
}
eaddr = saddr + size;
ui->output( " saddr=0x%llx eaddr=0x%llx\n",saddr, eaddr);
uint64_t baddr = saddr;
for (;saddr <= eaddr; saddr)
{
uint64_t v[4];
for (i = 0; i < 4; i++, saddr += 8)
{
#if defined(MEMORY_FLAT)
if (saddr >= mm1->get_size()-8)
{
ui->output( "MEM: phys address 0x%llx is beyond RAM \n", saddr);
return 1;
}
#endif
if (is_va)
{
uint64_t val = 0;
if(g_vcpu[cpuid]->read_mem(saddr, &val, 8) != 0)
{
ui->output( "MEM: cannot translate virtual address \n", saddr);
return 1;
}
else
{
v[i] = val;
}
}
else
{
v[i] = mm1->SMemory::ld64(saddr);
}
if (is_dis)
{
const int LSIZE = 128;
char iline[LSIZE];
uint32_t opc = uint32_t(v[i]>>32);
disassemble(opc, saddr , iline, LSIZE);
ui->output ("%i : 0x%llx: 0x%lx : %s \n", cpuid, saddr, opc, iline);
opc = uint32_t(v[i] & 0xffffffff);
disassemble(opc, saddr+4, iline, LSIZE);
ui->output ("%i : 0x%llx: 0x%lx : %s \n", cpuid, saddr+4, opc, iline);
}
else
{
if ( i==0 ) ui->output ("0x%llx:", baddr);
ui->output (" 0x%016llx", v[i] );
if ( i==3 ) ui->output ("\n");
}
}
baddr = saddr;
}
return 0;
} // mem_cmd_action()
/////////////////////////////////////////////
//
// ui memdump command
//
void print_memdump_cmd_usage()
{
ui->error ("Usage : memdump <filename> <start addr> <size> \n");
}
static int memdump_cmd_action (void *, int argc, char **argv)
{
uint64_t saddr = ~0ull;
uint64_t size = 0;
char *filename;
if (mm1 == NULL)
{
ui->error("MEM: RAM is not allocated yet \n");
return 0;
}
if (argc < 4)
{
print_memdump_cmd_usage();
return 1;
}
filename = argv[1];
saddr = strtoull(argv[2], NULL, 0);
size = strtoull(argv[3], NULL, 0);
if (!saddr || !size)
#ifdef MEMORY_SPARSE
if ((saddr == ~0ull) || (size==0))
#endif
#ifdef MEMORY_FLAT
if ((size==0) || (saddr > mm1->get_size()) || (saddr+size > mm1->get_size()))
#endif
{
ui->error("memdump: incorrect start address or size \n");
print_memdump_cmd_usage();
return 1;
}
ui->output("writing mem image to the file: %s, start address 0x%llx, size 0x%llx ... \n",
filename, saddr, size);
mm1->save(filename, saddr,size);
ui->output("----- MEMDUMP COMPLETED -----\n");
return 0;
} // memdump_cmd_action()