// ========== Copyright Header Begin ==========================================
// OpenSPARC T2 Processor File: pcie_dev.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 ============================================
genericPcieDev::genericPcieDev(confHeaderType htype
){
confSpace
= new pciConfSpace(0x1000);
msgDB
= new pciExpMsgUtility();
errDB
= new pciExpErrUtility();
genericPcieDev::genericPcieDev(){
confSpace
= new pciConfSpace(0x1000);
msgDB
= new pciExpMsgUtility();
errDB
= new pciExpErrUtility();
bool genericPcieDev::mapSpace(pcie_space space
, uint64_t base
, uint64_t size
){
return busIf
->busif_map(devif_getName(),space
, base
, size
);
bool genericPcieDev::unmapSpace(pcie_space space
,uint64_t base
){
return busIf
->busif_unmap(devif_getName(),space
, base
);
pcieCompleter
genericPcieDev::devif_memAccess(bool wr
, uint64_t addr
, addrMd_xactnType mode
, void * data
,\
uint16_t length
, uint8_t be
, uint16_t reqId
, tlp_X args
, SAM_DeviceId
*id
){
*id
= (dynamic_cast<Module
*>(this))->samId
;
return pcieCompleter(UR
,getId());
pcieCompleter
genericPcieDev::devif_ioAccess(bool wr
, uint32_t addr
, void * data
, uint8_t be
, \
uint16_t reqId
, uint16_t length
, tlp_X args
, SAM_DeviceId
*id
){
*id
= (dynamic_cast<Module
*>(this))->samId
;
return pcieCompleter(UR
,getId());
pcieCompleter
genericPcieDev::devif_msgAccess(uint8_t messageCode
, msgRouting route
, uint64_t tarIdOrAddr
, uint16_t reqId
, \
void * data
, uint16_t length
, \
uint16_t vendorId
, uint32_t vendor_data
, tlp_X args
, SAM_DeviceId
*id
){
*id
= (dynamic_cast<Module
*>(this))->samId
;
return pcieCompleter(UR
,getId());
pcieCompleter
genericPcieDev::devif_confAccess(bool wr
, uint32_t offset
, void * data
, \
uint8_t be
,uint16_t reqId
, addrMd_xactnType tType
, uint16_t length
, tlp_X args
, SAM_DeviceId
*id
){
assert((be
& 0xf0) == 0);
*id
= (dynamic_cast<Module
*>(this))->samId
;
confSpace
->confAccessByteE(offset
, wr
, (uint64_t *)data
, be
);
devif_confAccessCb(wr
,offset
,be
);
return pcieCompleter(SC
,getId());
// parse the busname, device, functions args
bool genericPcieDev::dev_parse_arg(const char *arg
){
if (argval("bus", arg
, &busName
)) {
Debug_more("%s: bus=<%s>\n", Here
, busName
);
} else if (argval("dev", arg
, &device
)) {
Debug_more("%s: dev=%d\n", Here
, device
);
} else if (argval("fun", arg
, &function
)) {
Debug_more("%s: fun=%d\n", Here
, function
);
Debug_err("%s: WARNING: unrecognized arg '%s'\n", Here
, arg
);
genericPcieDev::dev_check_args(){
Debug_err("%s: ERROR: must specify a PCI bus\n",devif_getName());
Debug_err("%s: ERROR: must specify device number\n", devif_getName());
}else if(function
== -1){
Debug_err("%s: ERROR: must specify function number\n", devif_getName());
genericPcieDev::dev_init_done(int dl
){
confSpace
->set_debug_level(dl
);
pciDebug::set_debug_level(dl
);
void genericPcieDev::pcieDevInfo(){
printf("bus=<%s> dev=%d fun=%d\n",busName
,device
,function
);
printf("configuration space reg info:\n");
// XXX the functions below "may" need some changes depending upon specific
// PCIE needs. Need to do more research to be sure.
bool genericPcieDev::dump(const char * dirname
,const char *file
){
filename
= new char[strlen(dirname
) + strlen(file
) + 6];
sprintf(filename
,"%s/%s.pcie",dirname
,file
);
fp
= fopen(filename
,"w");
Debug_err("%s pci dump. fopen failed for %s\n",devif_getName(), filename
);
for(int offset
= 0; offset
< 0x1000;){
if(offset
== PCI_CONF_COMM
){
cr
= confSpace
->getConfReg(offset
);
assert(offset
>= PCI_CONF_BASE0
&& offset
<= PCI_CONF_BASE5
);
pcieBaseAddrReg
*bar
= (pcieBaseAddrReg
*)cr
;
fprintf(fp
,"0x%08lx 0x%08lx 0x%08lx 0x%x %s %s\n",\
offset
,bar
->getVal(),bar
->getMask(),4,bar
->name
, "Low");
fprintf(fp
,"0x%08lx 0x%08lx 0x%08lx 0x%x %s %s\n",\
offset
+4,bar
->val_32To63
,bar
->mask_32To63
,4,bar
->name
, "High");
fprintf(fp
,"0x%08lx 0x%08lx 0x%08lx 0x%x %s\n",\
offset
,cr
->getVal(),cr
->getMask(),cr
->size
,cr
->name
);
cr
= confSpace
->getConfReg(PCI_CONF_COMM
);
fprintf(fp
,"0x%08lx 0x%08lx 0x%08lx 0x%x %s\n",\
PCI_CONF_COMM
,cr
->getVal(),cr
->getMask(),cr
->size
,cr
->name
);
genericPcieDev::dev_module_added(const char *target_name
){
if (busName
&& !strcmp(busName
, target_name
)) {
mmi_instance_t mod
= mmi_get_instance(busName
);
busIf
= (pcieBusIf
*)mmi_get_interface(mod
, PCIE_BUS_INTERFACE
);
Debug_err("%s: ERROR: can't find interface for PCIE bus <%s>\n", Here
, busName
);
}else if(!busIf
->busif_addDevice(devif_getName(), device
, function
)) {
Debug_err("%s: ERROR: could not add tp PCIE bus <%s>\n", Here
, busName
);
Debug_info("%s: bus <%s> connected: busif=%p\n", Here
, busName
, busIf
);
genericPcieDev::dev_module_deleted(const char *target_name
){
if (busName
&& !strcmp(busName
, target_name
)) {
Debug_info("%s: bus <%s> disconnected\n", Here
, busName
);
// to be called after initPci() has been called
bool genericPcieDev::restore(const char * dirname
,const char * file
){
char *filename
= new char[strlen(dirname
) + strlen(file
) + 6];
sprintf(filename
,"%s/%s.pcie",dirname
,file
);
FILE *fp
= fopen(filename
,"r");
Debug_err("%s pci restore. fopen failed for %s\n",devif_getBusName(), filename
);
for(uint32_t offset
= 0; offset
< 0x1000;){
if(offset
== PCI_CONF_COMM
){
cr
= confSpace
->getConfReg(offset
);
char * csr_offset
= strtok(buf
," ");
off
= strtoul(csr_offset
,0,0);
char * csr_value
= strtok(0," ");
val
= strtoul(csr_value
,0,0);
// if mis-match, it must be restoring from an old chechpoint,
// and this must be the command register, which always comes last.
assert(off
== PCI_CONF_COMM
);
printf("can't find PCIE register (offset = 0x%u) in %s.pcie, default value is used\n", offset
, file
);
char * csr_mask
= strtok(0," ");
char * csr_size
= strtok(0," ");
size
= strtoul(csr_size
,0,0);
cr
->write(val
,byte_offset
,size
);
devif_confAccessCb(true,offset
,size
);
} while (byte_offset
< cr
->size
);
// restore the command register
cr
= confSpace
->getConfReg(PCI_CONF_COMM
);
fscanf(fp
,"0x%x\t0x%x\t%s\n",&off
,&val
,name
);
cr
->write(val
,0,cr
->size
);
devif_confAccessCb(true,PCI_CONF_COMM
,cr
->size
);
void genericPcieDev::addMsiCap(uint8_t next_ptr
, uint16_t offset
, uint16_t reset_val
){
//if(msiCapOffset != -1){
// printf("can;t have more than 1 MSI capability/function");
confSpace
->addConfReg(new pciConfReg(" msi cap id ", 1, msiCapId
, 0x0, 0x0), offset
);
confSpace
->addConfReg(new pciConfReg(" msi next ptr ", 1, next_ptr
, 0x0,0x0), offset
+ MSI_NEXTPTR_OFFSET
);
confSpace
->addConfReg(new msiCapMsgCntrl(reset_val
),offset
+ MSI_MSGCNTRL_OFFSET
);
confSpace
->addConfReg(new pciConfReg(" msi addr0 ", 4, 0x0, 0xfffffffc, 0), offset
+ MSI_MSGADDR0_OFFSET
);
bool is64bit
= (reset_val
>> msiCapMsgCntrl::is64bitAddrCapable_bp
) & 1;
confSpace
->addConfReg(new pciConfReg(" msi addr1 ", 4, 0x0, 0xffffffff), offset
+ MSI_MSGADDR1_OFFSET
);
confSpace
->addConfReg(new pciConfReg(" msi data ", 2, 0x0, 0xffff), offset
+ MSI_MSGDATA64_OFFSET
);
confSpace
->addConfReg(new pciConfReg(" msi data ", 2, 0x0, 0xffff), offset
+ MSI_MSGDATA32_OFFSET
);
void genericPcieDev::addAERCap(uint16_t capOffset
, uint16_t nexCapPtr
){
aerCapOffset
= capOffset
;
snprintf(reg_descr
,100,"%s pciExpEnhncdCapHdr",devif_getName());
confSpace
->addConfReg(new pciExpEnhncdCapHdr(0x10001 | nexCapPtr
<< pciExpEnhncdCapHdr::nxtCapOffset_rbp
,0x0,0x0,reg_descr
),capOffset
);// AER capability, next ptr = 0, version = 1;
confSpace
->addConfReg(new pciExpAER_UnCorrErrStatReg(),capOffset
+ UNCORR_ERR_STAT_OFFSET
);
confSpace
->addConfReg(new pciConfReg("pciExpAER_UnCorrErrMask",4,0x0,aerUncorrErrBitMask
,0x0), capOffset
+ UNCORR_ERR_MASK_OFFSET
);
confSpace
->addConfReg(new pciConfReg("pciExpAER_UnCorrErrSev",4,0x0,aerUncorrErrBitMask
,0x0), capOffset
+ UNCORR_ERR_SEVERITY_OFFSET
);
confSpace
->addConfReg(new pciExpAER_CorrErrStatReg(), capOffset
+ CORR_ERR_STAT_OFFSET
);
confSpace
->addConfReg(new pciConfReg("pciExpAER_CorrErrMask",4, 0x0, aerCorrErrBitMask
,0x0),capOffset
+ CORR_ERR_MASK_OFFSET
);
confSpace
->addConfReg(new pciExpAER_ErrCapCntrlReg(0x1e0), capOffset
+ ADVANCED_ERR_CAPCNTRL_OFFSET
);
confSpace
->addConfReg(new pciConfReg("header log0",4,0x0,0xffffffff,0x0),capOffset
+ HDR_LOG_OFFSET
);
confSpace
->addConfReg(new pciConfReg("header log1",4,0x0,0xffffffff,0x0),capOffset
+ HDR_LOG_OFFSET
+ 4);
confSpace
->addConfReg(new pciConfReg("header log2",4,0x0,0xffffffff,0x0),capOffset
+ HDR_LOG_OFFSET
+ 8);
confSpace
->addConfReg(new pciConfReg("header log3",4,0x0,0xffffffff,0x0),capOffset
+ HDR_LOG_OFFSET
+ 12);
pcieCompleter
genericPcieDev::routeMsg(pciExpMsgCode code
){
msgRouting r
= msgDB
->msgCode2Routing(code
);
assert( r
!= routed_by_addr
);
assert( r
!= routed_by_id
);
// bridges should not use this function anymore. Only leaf devices are
SAM_DeviceId myId
= (dynamic_cast<Module
*>(this))->samId
;
return busIf
->busif_msgAccess((uint8_t)code
, r
, getId(),&myId
);
// handle an error condition from the perspective of a receiver/completer
// of a TLP or as the detecting agent( eg trainingErr) of an
// error condition. the routine sets the error bits and sends error messages
// as applicable. This routine also takes care of AER regs if implemented
// it is assumed that this device is not a root port
pcieCompleter
genericPcieDev::processErrorRcvrCmpltr(pciExp_Error error
, uint32_t * header
){
// implement the flow chart section 6.2.5 of rev 1.0a
pciExpErrorType errType
= errDB
->error2DfltErrType(error
);
pciConfReg
* devStatReg
= confSpace
->getConfReg(pciExpCapOffset
+ PCI_EXP_DEV_STAT_OFFSET
);
pciConfReg
* devCntrlReg
= confSpace
->getConfReg(pciExpCapOffset
+ PCI_EXP_DEV_CTRL_OFFSET
);
pciConfReg
* cmdReg
= confSpace
->getConfReg(PCI_CONF_COMM
);
pciConfReg
* statReg
= confSpace
->getConfReg(PCI_CONF_STAT
);
if(errType
== errCorrectable
){
devStatReg
->setBit(pciExpDevStatReg::corrErrDet_bp
);
pciConfReg
* corrErrStatReg
= confSpace
->getConfReg(aerCapOffset
+ CORR_ERR_STAT_OFFSET
);
corrErrStatReg
->setBit(errDB
->error2RegBitPos(error
));
pciConfReg
* corrErrMaskReg
= confSpace
->getConfReg(aerCapOffset
+ CORR_ERR_MASK_OFFSET
);
bool errorIsMasked
= corrErrMaskReg
->getBit(errDB
->error2RegBitPos(error
));
return pcieCompleter(SIM_OK
);
if(devCntrlReg
->getBit(pciExpDevCntrlReg::corrErrRepEn_bp
))
return routeMsg(MSG_ERR_COR
);
return pcieCompleter(SIM_OK
);
// get the severity from severity register if aer is supported
// else get the defualt severity for this error.
pciConfReg
* unCorrSevReg
= confSpace
->getConfReg(aerCapOffset
+ UNCORR_ERR_SEVERITY_OFFSET
);
severityFatal
= unCorrSevReg
->getBit(errDB
->error2RegBitPos(error
));
severityFatal
= errType
== errUnCorrectable_fatal
? true:false;
devStatReg
->setBit(pciExpDevStatReg::ftlErrDet_bp
);
devStatReg
->setBit(pciExpDevStatReg::nonFtlErrDet_bp
);
// set the bits in dev stat reg in case its a UR
if(error
== unsupportedRequest
)
devStatReg
->setBit(pciExpDevStatReg::unSprtdReqDet_bp
);
statReg
->setBit(pcieStatusReg::DETECTED_PARITY_ERROR
);
if(error
== completerAbort
)
statReg
->setBit(pcieStatusReg::SIGNALED_TARGET_ABORT
);
pciConfReg
* unCorrErrStatReg
= confSpace
->getConfReg(aerCapOffset
+ UNCORR_ERR_STAT_OFFSET
);
uint32_t unCorrErrStatOldVal
= unCorrErrStatReg
->getVal();
unCorrErrStatReg
->setBit(errDB
->error2RegBitPos(error
));
pciConfReg
* unCorrErrMaskReg
= confSpace
->getConfReg(aerCapOffset
+ UNCORR_ERR_MASK_OFFSET
);
bool errorIsMasked
= unCorrErrMaskReg
->getBit(errDB
->error2RegBitPos(error
));
return pcieCompleter(SIM_OK
);
if(unCorrErrStatOldVal
== 0){
// error is unmasked and this is the first error, set the
// first error pointer bit field
pciConfReg
* capCtrlReg
= confSpace
->getConfReg(aerCapOffset
+ ADVANCED_ERR_CAPCNTRL_OFFSET
);
capCtrlReg
->setRange(pciExpAER_ErrCapCntrlReg::firstErrPointer_lbp
, \
pciExpAER_ErrCapCntrlReg::firstErrPointer_rbp
,errDB
->error2RegBitPos(error
));
// need to log the header as well
for(int i
= 0; i
< 4; i
++)
confSpace
->getConfReg(aerCapOffset
+ HDR_LOG_OFFSET
+ i
* 4)->setVal(*(header
+ i
));
if(error
== unsupportedRequest
){
if(devCntrlReg
->getBit(pciExpDevCntrlReg::unsprtdReqRepEn_bp
) == 0)
return pcieCompleter(SIM_OK
);
return routeMsg(MSG_ERR_FATAL
);
return routeMsg(MSG_ERR_NONFATAL
);
int serrEnable
= cmdReg
->getBit(pcieCommandReg::SERR_ENABLE
);
statReg
->setBit(pcieStatusReg::SIGNALED_SYSTEM_ERROR
);
if(serrEnable
|| devCntrlReg
->getBit(pciExpDevCntrlReg::fatalErrRepEn_bp
))
return routeMsg(MSG_ERR_FATAL
);
if(serrEnable
|| devCntrlReg
->getBit(pciExpDevCntrlReg::nonFatalErrRepEn_bp
))
return routeMsg(MSG_ERR_NONFATAL
);
return pcieCompleter(SIM_OK
);
// return 0 on success, -1 on error
int genericPcieDev::handleCompletion(pcieCompleter C
){
pciConfReg
* statReg
= confSpace
->getConfReg(PCI_CONF_STAT
);
if(C
.status
== SIM_OK
)// non posted requests
statReg
->setBit(pcieStatusReg::RECEIVED_MASTER_ABORT
);
statReg
->setBit(pcieStatusReg::RECEIVED_TARGET_ABORT
);
// the next two are not part of specs. the target returns it
// so as to aid functional simulation (eg there is no way to timeout
// in the initiator in this kind of model)
else if(C
.status
== CMPL_TO
)
processErrorRcvrCmpltr(completionTimeout
);
else if(C
.status
== CMPL_POISONED
){
pciConfReg
* cmdReg
= confSpace
->getConfReg(PCI_CONF_COMM
);
if(cmdReg
->getBit(pcieCommandReg::PARITY_ERR_EN
))
statReg
->setBit(pcieStatusReg::MASTER_DATA_PARITY_ERROR
);
else if(C
.status
== SIM_FAIL
){
printf("ERROR: internal error in the simulator. returned SIM_FAIL\n");
printf("ERROR: unknow completion <%d >received\n", C
.status
);
///////////////////////////////////////////////////////////////////////////////
/*****************base address register*********/
pcieBaseAddrReg::pcieBaseAddrReg(const char * name
, genericPcieDev
* d
,uint64_t spaceSize
,pcie_space sp
, bool ismem32
, bool isPrefetch
): \
pciConfReg(name
,4,0,0 ,(void*)d
){
printf("baseAddrReg constructor error: must provide pointer to genericPciDev\n");
printf("baseAddrReg constructor error: size must be greater than or equal 16\n");
int first_set_bit
= ffs((uint32_t)spaceSize
);
first_set_bit
= ffs((uint32_t) (spaceSize
>> 32));
mask_32To63
= (uint32_t)-1 << (first_set_bit
- 1);
mask_32To63
= 0xffffffff;
mask
= (uint32_t)-1 << (first_set_bit
- 1);
this->isPrefetchable
= isPrefetch
;
this->mapSize
= spaceSize
;
mask_32To63
= 0xffffffff;
val
= bartype
| PREFETCH
;
uint8_t pcieBaseAddrReg::write(uint32_t buf
, uint8_t byte_offset
, uint8_t bytes_to_write
){
// simpler assumption for now of whole reg reads.
// if assert ever fails then add support for (bytes_to_write <= 4 == true) etc.
assert(bytes_to_write
== 4);
assert(byte_offset
== 0 or byte_offset
== 4);
oldVal
= val
| (uint64_t)val_32To63
<< 32;
printf("Name<%s>,size<0x%x>offset<0x4>mask<0x%lx>value<0x%lx -> 0x%lx>\n",name
,size
,mask
,val_32To63
,buf
& mask_32To63
);
val_32To63
= buf
& mask_32To63
;
pciConfReg::write(buf
, byte_offset
, bytes_to_write
);
void pcieBaseAddrReg::reMap(){
//read the command register
confReg
= ((genericPcieDev
*)data
)->confSpace
->getConfReg(PCI_CONF_COMM
);
confReg
->read(&commandRegVal
,confReg
->size
);
bool space_enabled
= commandRegVal
& 1 << pcieCommandReg::MEM_SPACE
;
((genericPcieDev
*)data
)->unmapSpace(PCIE_MEM
,oldVal
& ~0xf);
((genericPcieDev
*)data
)->mapSpace(PCIE_MEM
,val
&~0xf,mapSize
);
((genericPcieDev
*)data
)->mapSpace(PCIE_MEM
,(val
|(uint64_t)val_32To63
<< 32) &~0xf ,mapSize
);
}else if(space
== PCIE_IO
){
bool space_enabled
= (commandRegVal
& 1 << pcieCommandReg::IO_SPACE
);
((genericPcieDev
*)data
)->unmapSpace(PCIE_IO
,oldVal
& ~0xf);
((genericPcieDev
*)data
)->mapSpace(PCIE_IO
,val
& ~0xf , mapSize
);
bool pcieBaseAddrReg::map(){
isMapped
= ((genericPcieDev
*)data
)->mapSpace(space
,(val
|(uint64_t)val_32To63
<< 32) & ~0xf , mapSize
);
isMapped
= ((genericPcieDev
*)data
)->mapSpace(space
,val
& ~0xf , mapSize
);
bool pcieBaseAddrReg::unmap(){
isMapped
= ((genericPcieDev
*)data
)->unmapSpace(space
,(val
|(uint64_t)val_32To63
<< 32) & ~0xf);
isMapped
= ((genericPcieDev
*)data
)->unmapSpace(space
,val
& ~0xf);
/********************Command reg************************/
uint8_t pcieCommandReg::write(uint32_t buf
, uint8_t byte_offset
, uint8_t bytes_to_write
){
assert(byte_offset
== 0);
buf
&= ~((int32_t)-1 << ret
* 8);
//uint32_t changed_bits = val ^ (mask & buf);
uint32_t new_val
= val
| (mask
& buf
);
uint32_t changed_bits
= new_val
^ val
;
printf("Name<%s>,size<0x%x>mask<0x%lx>value<0x%lx -> 0x%lx>\n",name
,size
,mask
,val
,new_val
);
if(changed_bits
& 1 << IO_SPACE
){ //I/O space map bit has changed
if(val
& 1 << IO_SPACE
) //bit set to 1
unMapSpace(PCIE_IO
); //bit set to 0
if(changed_bits
& 1 << MEM_SPACE
){
if(val
& 1 << MEM_SPACE
){ //bit set to 1
void pcieCommandReg::mapSpace(pcie_space spc
){
//base address regs range from 0x10 to 0x24
pcieBaseAddrReg
* baseAddr
;
for(int i
= 0x10; i
< 0x28; i
+=4){
baseAddr
= dynamic_cast<pcieBaseAddrReg
*>(((genericPcieDev
*)data
)->confSpace
->getConfReg(i
));
if(baseAddr
&& baseAddr
->space
== spc
){
if(baseAddr
->bartype
== pcieBaseAddrReg::MEM64
)
void pcieCommandReg::unMapSpace(pcie_space spc
){
//base address regs range from 0x10 to 0x24
pcieBaseAddrReg
* baseAddr
;
for(int i
= 0x10; i
< 0x28; i
+=4){
baseAddr
= dynamic_cast<pcieBaseAddrReg
*>(((genericPcieDev
*)data
)->confSpace
->getConfReg(i
));
if(baseAddr
&& baseAddr
->space
== spc
){
if(baseAddr
->bartype
== pcieBaseAddrReg::MEM64
)