Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / devices / ioram / ioram.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: ioram.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 ============================================
#include "ioram.h"
int iomem_ui_cmd(void * obj, int argc, char * argv[]){
IOMem * i = (IOMem *)obj;
i->handle_ui(argc, argv);
return 0;
}
static const char * iomem_help = "SAM IOMem module\n \
Sysconf format is :\n \
sysconf iosram <instance name> start_pa=<addr> size=<size> [file=<name> [addr=<load_addr>]] [rw|ro|wo] [sparse|flat]\n \
The default is zero filled RW memory with Flat impl.\n \
For \'file\' , \'addr\' must be absolute PA. Default \'addr\'=\'start_pa\'\n \
The filename must have a .bin, .img or .elf extension. .img files need not supply \'addr\'\n \
For UI help type <instance name>";
const char *
Module::get_help_string(){
return iomem_help;
}
Module *
Module::create(const char *_modname, const char *_instance_name){
return new IOMem(_modname, _instance_name);
}
/***********************************************************/
IOMem::IOMem(const char *_modname, const char *_instance_name)
: Module(_modname, _instance_name){
sAddr = eAddr = -1;
mem_size = 0;
ft = NONE; // zero filled
at = RW; // allow read write
mt = FLAT; // FLAT memory model
loadFile = 0;
loadAddr = 0;
memIf = 0;
}
// one line help
const char *IOMem::get_help(){
return get_help_string();
}
// print info about the module
void IOMem::modinfo(){
printf("%s: SAM IOMem module\n", getName());
printf("start paddr <0x%llx, end paddr <0x%llx>\n",sAddr,eAddr);
printf("loaded with file <%s> at addr <%llx>\n", loadFile?loadFile:"none - zero filled", loadAddr);
printf("s/w access <%s>, implementation <%s model>\n", \
(at == RW ? "rw":(at == RO ? "RO":"WO")), mt == SPARSE ? "sparse":"flat" );
printf("for UI help type %s\n",getName());
return;
}
// parse the arguments for ram module
bool
IOMem::parse_arg(const char * arg){
if(argval("start_pa",arg,&sAddr)){
debug_more("%s: start_pa = %llx\n",getName(),sAddr);
return true;
}else if(argval("size",arg,&mem_size)){
debug_more("%s: size = %llx\n",getName(),mem_size);
return true;
}else if(argval("file",arg,&loadFile)){
debug_more("%s: load file = %s\n",getName(), loadFile);
const char * p = loadFile + strlen(loadFile) - 4;
if(!strcmp(p,".bin"))
ft = BIN;
else if(!strcmp(p,".img"))
ft = IMG;
else if(!strcmp(p,".elf"))
ft = ELF;
else
debug_err("%s: could not identify file type from extension. memory zero filled\n", getName());
return true;
}else if(argval("addr",arg,&loadAddr)){
debug_more("%s: file load address = %llx\n", loadAddr);
}else if(!strcmp(arg,"rw")){
at = RW;
return true;
}else if(!strcmp(arg,"ro")){
at = RO;
return true;
}else if(!strcmp(arg,"wo")){
at = WO;
return true;
}else if(!strcmp(arg,"sparse")){
mt = SPARSE;
return true;
}else if(!strcmp(arg,"flat")){
mt = FLAT;
return true;
}
return false;
}
// check if necessary arguments have been supplied in
// configuration file. Initialization fails if returns false.
bool
IOMem::check_args(){
if(sAddr == -1){
debug_err("%s: ERROR: must specify start address\n", getName());
return false;
}else if(mem_size == 0){
debug_err("%s: ERROR: must specify size\n", getName());
return false;
}else
return true;
}
// all arguments have been parsed and checked.
void
IOMem::init_done(){
// 1.
// align the size to an 8 byte boundary.
if(mem_size % 8 != 0)
mem_size = (mem_size/8)*8 + 8;
eAddr = sAddr + mem_size - 1;
if(loadAddr == 0)
loadAddr = sAddr;
else if(addrInvalid(loadAddr)){
debug_err("file load addr invalid, setting to %llx\n",sAddr);
loadAddr = sAddr;
}
if(mmi_map_physio(sAddr,mem_size, this , iomemLdSt) != 0){
debug_err("%s: failed to map the addresses to IOMem \n exit...", getName());
exit(1); // fatal error
}
if( mt == SPARSE )
memIf = new SparseMemory(mem_size);
else if( mt == FLAT ){
debug_err("%s: Flat memory not yet implemented. Using sparse model\n",getName());
// memIf = new FlatMemory(mem_size);
memIf = new SparseMemory(mem_size);
}
memIf->setId(getName());
if(ft == BIN)
memIf->load_bin(loadFile,loadAddr - sAddr);
else if(ft == IMG)
memIf->load_img(loadFile, sAddr);
else if(ft == ELF)
memIf->load_elf(loadFile,loadAddr - sAddr);
mmi_register_instance_cmd(getInstance(),iomem_help,iomem_ui_cmd);
return;
}
// callback function. called whenever there is a ld/st in the address
// range from sAddr to eAddr, hence no range check is needed here
int iomemLdSt(uint32_t cpuid, void* obj, uint64_t paddr, mmi_bool_t wr, uint32_t size, \
uint64_t* buf, uint8_t bytemask){
IOMem* s = (IOMem*)obj;
return s->iomemHandler(cpuid,paddr,wr,size,buf,bytemask);
}
// handle the r/w access from cpu
int
IOMem::iomemHandler(uint32_t cpuid,uint64_t paddr, mmi_bool_t wr, uint32_t size, uint64_t* buf, uint8_t bytemask){
int error;
if(wr)
error = iomemWrite(paddr - sAddr,*buf,size);
else
error = iomemRead(paddr - sAddr,buf,size);
// print a debug message if debug_level > 1
debug_more("IOMem %s:%s xactn from cpu %d at address %llx, size %d, buf %llx\n",\
getName(),wr?"write":"read",cpuid,paddr,size,*buf);
return error;
}
int IOMem::iomemWrite(uint64_t addr, uint64_t buf, int size){
if(at == RO){
debug_err("%s - write @ offset %llx : configured as RO memory\n", getName(),addr);
return -1;
}
switch(size){
case 1:
memIf->st8(addr,buf);
return 0;
case 2:
memIf->st16(addr,buf);
return 0;
case 4:
memIf->st32(addr,buf);
return 0;
case 8:
memIf->st64(addr,buf);
return 0;
default:
debug_err("%s write: unsupported size %d, ... ignored\n", getName(), size);
return 0;
}
}
int IOMem::iomemRead(uint64_t addr, uint64_t * buf, int size){
switch(size){
case 1:
*buf = memIf->ld8u(addr);
return 0;
case 2:
*buf = memIf->ld16u(addr);
return 0;
case 4:
*buf = memIf->ld32u(addr);
return 0;
case 8:
*buf = memIf->ld64(addr);
return 0;
default:
debug_err("%s read: unsupported size %d, ... ignored\n", getName(), size);
return 0;
}
}
void IOMem::ui_cmd_usage(){
printf("ui format: %s <command> <command args> ...\n",getName());
printf("%s supports following ui commands\n",getName());
printf(" write <addr> <value> <size> \n\
write \'size\' bytes to memory at address \'addr\'where, size=[1|2|4|8]\n");
printf(" read <addr> <size>\n\
read \'size\' bytes from memory at address \'addr\'where, size=[1|2|4|8]\n");
printf(" dis <addr> [<n_instr>]\n\
disassemble \'n_instr\' instructions (default 1) starting from address \'addr\'\n");
printf(" dump <addr> <size> [<format>]\n\
dump \'size\' bytes of memory on stdout\n\
where \'format\' is [x|d|c|o], default x\n");
printf(" save <filename> [<addr> [<size>]]\n\
save the memory contents in \'filename\' starting from \'addr\' for \'size\' bytes.\n\
If no \'addr\' and \'size\' arguments provided, save all\n\
If \'addr\' is supplied, but no \'size\', save till the end\n");
printf(" All numerical values are expected either in decimal/octal/hex \n");
printf(" The \'addr\' argument is an absolute physical address\n");
}
void IOMem::handle_ui(int argc, char * argv[]){
if(argc == 1){
ui_cmd_usage();
return;
}else{
if(!strcmp(argv[1],"write"))
ui_write(&argv[2]);
else if(!strcmp(argv[1],"read"))
ui_read(&argv[2]);
else if(!strcmp(argv[1],"save"))
ui_save(&argv[2]);
else if(!strcmp(argv[1],"dump"))
ui_dump(&argv[2]);
else if(!strcmp(argv[1],"dis"))
ui_dis(&argv[2]);
else
debug_err("%s: unsupported UI %s\n",getName(),argv[1]);
}
return;
}
extern int errno;
bool IOMem::getNum(const char * s, uint64_t * val){
errno = 0;
if(!s)
return false;
*val = strtoll(s,0,0);
if(errno)
return false;
return true;
}
bool IOMem::numArgsOK(int argc, char * args[]){
for(int i = 0; i < argc; i++)
if(!args[i])
return false;
return true;
}
bool IOMem::addrInvalid(uint64_t addr){
if(addr > eAddr)
return true;
if(addr < sAddr)
return true;
return false;
}
void IOMem::ui_write(char * args[]){
if(!numArgsOK(3,args)){
debug_err("%s write: wrong number of command arguments\n", getName());
return;
}
uint64_t size, val, addr;
if(!getNum(args[0], &addr))
goto ui_write_err;
if(!getNum(args[1], &val))
goto ui_write_err;
if(!getNum(args[2], &size))
goto ui_write_err;
if(addrInvalid(addr)){
debug_err("%s write: addr out of range\n",getName());
return;
}
if(size != 1 && size != 2 && size != 4 && size != 8){
debug_err("%s write: unsupported write size\n", getName());
return;
}
addr -= sAddr;
iomemWrite(addr, val, size);
return;
ui_write_err:
debug_err("%s write: error parsing arguments\n",getName());
}
void IOMem::ui_read(char * args[]){
if(!numArgsOK(2,args)){
debug_err("%s read: wrong number of command arguments\n", getName());
return;
}
uint64_t size, addr, val = 0;
if(!getNum(args[0], &addr))
goto ui_read_err;
if(!getNum(args[1], &size))
goto ui_read_err;
if(addrInvalid(addr)){
debug_err("%s read: addr out of range\n",getName());
return;
}
if(size != 1 && size != 2 && size != 4 && size != 8){
debug_err("%s read: unsupported read size\n", getName());
return;
}
addr -= sAddr;
iomemRead(addr, &val , size);
printf("0x%llx\n",val);
fflush(stdout);
return;
ui_read_err:
debug_err("%s read: error parsing arguments\n",getName());
}
void IOMem::ui_dump(char * args[]){
if(!numArgsOK(2,args)){
debug_err("%s dump: wrong number of command arguments\n", getName());
return;
}
uint64_t size, addr;
if(!getNum(args[1], &size))
goto ui_dump_err;
if(!getNum(args[0], &addr))
goto ui_dump_err;
if(addrInvalid(addr)){
debug_err("%s dump: addr out of range\n",getName());
return;
}
if(addr + size -1 > eAddr){
debug_err("%s dump : cannot dump beyond segment size\n",getName());
return;
}
addr -= sAddr;
size = size % 16 ? (size/16+1) * 16 : size;
char * format = strdup(args[2] ? args[2]: "x");
if(format[0] != 'x' && format[0] != 'o' && format[0] != 'd' && format[0] != 'c'){
debug_err("%s dump: unrecognized format argument. Using hex\n", getName());
free(format);
format = strdup("x");
}
if(!strcmp(format,"c")){
uint64_t c;
int j = 0;
for( int i = 0; i < size; i++, j++){
if( (j >= 16) && (j % 16 == 0) )
printf("\n");
iomemRead(addr + i, &c, 1);
if(j % 16 == 0)
printf("%010o ",i);
if(isprint(c))
printf("%3c ",c);
else
printf("%03lld ",c);
}
printf("\n");
fflush(stdout);
return;
}
for( int i = 0, j = 0; i < size; i+=2 , j+=2 ){
uint64_t val;
iomemRead(addr + i, &val, 2);
if( (j >= 16) && (j % 16 == 0) )
printf("\n");
if(j % 16 == 0)
printf("%010o ",i);
if(!strcmp(format,"x"))
printf("%04llx ",val);
else if(!strcmp(format,"o"))
printf("%06llo ",val);
else
printf("%05lld ",val);
}
printf("\n");
fflush(stdout);
return;
ui_dump_err:
debug_err("%s dump: error parsing arguments\n",getName());
}
void IOMem::ui_dis(char * args[]){
if(!numArgsOK(1,args)){
debug_err("%s dis: wrong number of command arguments\n", getName());
return;
}
uint64_t n_instr, addr;
if(!getNum(args[0], &addr))
goto ui_dis_error;
if(args[1]){
if(!getNum(args[1], &n_instr))
goto ui_dis_error;
}else
n_instr = 1;
if(addrInvalid(addr)){
debug_err("%s dis: addr out of range\n",getName());
return;
}
if((addr + n_instr*4 > eAddr)){
debug_err("%s dis: addr + n_instr*4 out of range\n",getName());
return;
}
addr -= sAddr;
for(int i = 0; i < n_instr; i++){
uint64_t val;
const int LSIZE = 128;
char iline[LSIZE];
iomemRead(addr + i*4, &val, 4);
disassemble(val,sAddr + addr + i*4,iline,LSIZE);
printf("0x%-20llx: 0x%-10llx : %s \n", addr + i*4, val, iline);
}
fflush(stdout);
return;
ui_dis_error:
debug_err("%s dis: error parsing arguments\n",getName());
}
void IOMem::ui_save(char * args[]){
uint64_t addr, sz;
if(!numArgsOK(1,args)){
debug_err("%s save: wrong number of command arguments\n", getName());
return;
}
if(!getNum(args[0], &addr))
addr = sAddr;
else if(!getNum, args[1],&sz)
sz = mem_size;
if(addrInvalid(addr)){
debug_err("%s save: invalid addr value\n",getName());
return;
}else if(addr + mem_size - 1 > eAddr){
debug_err("%s save: invalid addr/size values\n",getName());
return;
}
printf("%s save: saving to file %s, start_pa=<%llx>, size=<%llx>\n",getName(),args[0], addr, sz);
memIf->save(args[0],addr - sAddr,sz);
return;
}
bool IOMem::dump(FILE *fp){
assert(fp);
memIf->dump(fp);
return true;
}
bool IOMem::restore(FILE *fp){
assert(fp);
memIf->restore(fp);
return true;
}