Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / devices / sas / sas.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: sas.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 ============================================
/*
* "sas.cc" LSI SAS1064E PCIE to 4-Port Serial Attached
* SCSI Controller Simulator
* Controller-Host interface
*
* Copyright (C) 2007 Sun Microsystems, Inc.
* All rights reserved.
*
*/
#include "sas.h"
#include "dr.h"
#include "fileutil.h"
// io space size
#define IO_SIZE 256
// memory space size
#define MEM0_SIZE 1024
#define MEM1_SIZE 65536
// power management capability id register offset
#define PCI_CONF_PM_CAP_ID 0x44
// MSI capability register offset
#define PCI_CONF_MSI_CAP_ID 0x50
// SAS simulator general information
static const char *SAS1064E_Help = "LSISAS1064E PCI Express to 4-port Serial Attached SCSI Controller";
// SAS simulator version
static const char *SAS1064E_Version = "1.00";
extern "C" char *overlaydir;
extern void overlaydir_cleanup();
extern char *get_restore_dir();
SAS::SAS(const char *modname, const char *instance_name)
: Module(modname, instance_name) {
debug_info("SAS1064E: creating instance %s\n", instance_name);
_port_enabled = (uint8_t *)calloc(IOC_NUM_PORTS, sizeof(uint8_t));
_earliest_serving_time = (int64_t *)calloc(IOC_NUM_PORTS, sizeof(int64_t));
_mpt_reg = (mpt_reg_t *)calloc(1, sizeof(mpt_reg_t));
_ioc_conf = (ioc_conf_t *)calloc(1, sizeof(ioc_conf_t));
_hdshk_msg_req_buf = (uchar_t *)calloc(1, HDSHK_MSG_SIZE);
_hdshk_msg_reply_buf = (uchar_t *)calloc(1, HDSHK_MSG_SIZE);
_hdshk_msg_req_size = 0;
_hdshk_msg_req_recv = 0;
_hdshk_msg_reply_size = 0;
_hdshk_msg_reply_send = 0;
_request_queue_size = 0;
while (!_reply_post_queue.empty())
_reply_post_queue.pop();
while (!_reply_free_queue.empty())
_reply_free_queue.pop();
_etable.init();
pthread_mutex_init(&_reply_queue_mutex, NULL);
// ioc is in ready state after power-on self-initialization
_mpt_reg->doorbell = MPI_IOC_STATE_READY;
}
SAS::~SAS() {
debug_info("SAS1064E: deleting instance %s\n", instance_name);
free(_port_enabled);
free(_earliest_serving_time);
free(_mpt_reg);
free(_ioc_conf);
free(_hdshk_msg_req_buf);
free(_hdshk_msg_reply_buf);
pthread_mutex_destroy(&_reply_queue_mutex);
for (int i = 0; i < IOC_NUM_PORTS; i++) {
if (_disk[i]) {
_disk[i]->delete_overlays();
free(_disk[i]);
}
}
}
const char *SAS::get_help() {
return Module::get_help_string();
}
const char *SAS::get_version() {
return SAS1064E_Version;
}
bool SAS::parse_arg(const char *arg) {
if (argval("targets", arg, &fname)){
debug_info("%s: targets=<%s>\n", HERE, fname);
return true;
}
return dev_parse_arg(arg);
}
bool SAS::check_args(){
if (dev_check_args()) {
return true;
}
return false;
}
void SAS::initPci() {
char reg_descr[100];
snprintf(reg_descr,100,"%s LSISAS1064E Vendor ID", getName());
confSpace->addConfReg(new pciConfReg(reg_descr,2,0x1000,0x0000,0x0),PCI_CONF_VENID);
snprintf(reg_descr,100,"%s LSISAS1064E Device ID", getName());
confSpace->addConfReg(new pciConfReg(reg_descr,2,0x0056,0x0000,0x0),PCI_CONF_DEVID);
snprintf(reg_descr,100,"%s LSISAS1064E Command", getName());
confSpace->addConfReg(new pcieCommandReg(reg_descr,(genericPcieDev *)this,0x0757),PCI_CONF_COMM);
snprintf(reg_descr,100,"%s LSISAS1064E Status", getName());
confSpace->addConfReg(new pcieStatusReg(reg_descr,(genericPcieDev *)this,0x0230,0xff38),PCI_CONF_STAT);
snprintf(reg_descr,100,"%s LSISAS1064E Rev Id",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,1,0x03,0x00),PCI_CONF_REVID);
snprintf(reg_descr,100,"%s LSISAS1064E prog class",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,1,0x00,0x00),PCI_CONF_PROGCLASS);
snprintf(reg_descr,100,"%s LSISAS1064E Sub Class",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,1,0x00,0x00),PCI_CONF_SUBCLASS);
snprintf(reg_descr,100,"%s LSISAS1064E Base Class",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,1,0x01,0x00),PCI_CONF_BASCLASS);
snprintf(reg_descr,100,"%s LSISAS1064E Cache Line Size",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,1,0x00,0xf8),PCI_CONF_CACHE_LINESZ);
snprintf(reg_descr,100,"%s LSISAS1064E Latency Timer",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,1,0x00,0xf0),PCI_CONF_LATENCY_TIMER);
snprintf(reg_descr,100,"%s LSISAS1064E Header Type",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,1,0x00,0x00),PCI_CONF_HEADER);
snprintf(reg_descr,100,"%s LSISAS1064E BAR0 - IO",getName());
confSpace->addConfReg(new pcieBaseAddrReg(reg_descr,(genericPcieDev *)this,IO_SIZE,PCIE_IO, false, false),PCI_CONF_BASE0); //256 bytes
snprintf(reg_descr,100,"%s LSISAS1064E BAR1 MEM64 0",getName());
confSpace->addConfReg(new pcieBaseAddrReg(reg_descr,(genericPcieDev *)this,MEM0_SIZE,PCIE_MEM, false, false),PCI_CONF_BASE1); //1024 bytes
snprintf(reg_descr,100,"%s LSISAS1064E BAR3 MEM64 1",getName());
confSpace->addConfReg(new pcieBaseAddrReg(reg_descr,(genericPcieDev *)this,MEM1_SIZE,PCIE_MEM, false, false),PCI_CONF_BASE3); //64k bytes
snprintf(reg_descr,100,"%s LSISAS1064E susbsystem vendor id",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,2,0x0000,0x0000),PCI_CONF_SUBVENID);
snprintf(reg_descr,100,"%s LSISAS1064E susbsystem id",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,2,0x0000,0x0000),PCI_CONF_SUBSYSID);
snprintf(reg_descr,100,"%s LSISAS1064E ROM base addr",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,4,0x00000000,0x0),PCI_CONF_ROM);
snprintf(reg_descr,100,"%s LSISAS1064E capabilities pointer",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,1,PCI_CONF_PM_CAP_ID,0x00),PCI_CONF_CAP_PTR);
snprintf(reg_descr,100,"%s LSISAS1064E Intr Line",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,1,0x00,0xff),PCI_CONF_ILINE);
snprintf(reg_descr,100,"%s LSISAS1064E Intr Pin",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,1,0x01,0x00),PCI_CONF_IPIN);
snprintf(reg_descr,100,"%s LSISAS1064E Min Grant",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,1,0x00,0x00),PCI_CONF_MIN_G);
snprintf(reg_descr,100,"%s LSISAS1064E Max Lat",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,1,0x00,0x00),PCI_CONF_MAX_L);
// power management capability
snprintf(reg_descr,100,"%s LSISAS1064E Power Mgmt Cap ID",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,1,0x01,0x00),PCI_CONF_PM_CAP_ID);
snprintf(reg_descr,100,"%s LSISAS1064E Power Mgmt Next Pointer",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,1,PCI_CONF_MSI_CAP_ID,0x00),PCI_CONF_PM_CAP_ID+PCI_CAP_NEXT_PTR);
snprintf(reg_descr,100,"%s LSISAS1064E Power Mgmt Capabilities",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,2,0x0202,0x0000),PCI_CONF_PM_CAP_ID+PCI_PMCAP);
snprintf(reg_descr,100,"%s LSISAS1064E Power Mgmt CSR",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,2,0x0000,0x0000),PCI_CONF_PM_CAP_ID+PCI_PMCSR);
snprintf(reg_descr,100,"%s LSISAS1064E Power Mgmt CSR Bridge Support Extension",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,1,0x00,0x00),PCI_CONF_PM_CAP_ID+PCI_PMCSR_BSE);
snprintf(reg_descr,100,"%s LSISAS1064E Power Mgmt Data",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,1,0x00,0x00),PCI_CONF_PM_CAP_ID+PCI_PMDATA);
// MSI capability
addMsiCap(0x0,PCI_CONF_MSI_CAP_ID,0x0080);
snprintf(reg_descr,100,"%s LSISAS1064E MSI Reserved",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,1,0x00,0x00),PCI_CONF_MSI_CAP_ID+0xe);
snprintf(reg_descr,100,"%s LSISAS1064E MSI Mask Bits",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,4,0x00000000,0xffffffff),PCI_CONF_MSI_CAP_ID+0x10);
snprintf(reg_descr,100,"%s LSISAS1064E MSI Pending Bits",getName());
confSpace->addConfReg(new pciConfReg(reg_descr,4,0x00000000,0x00000000),PCI_CONF_MSI_CAP_ID+0x14);
}
void SAS::handle_ui_cmd(int argc, char * argv[]) {
if (argc == 1) {
printf("%s supports following UI commands\n",getName());
printf("%s debug [<level>]\n\
set the debug level for debug prints to \'level\'\n\
if \'level\' not provided, print current debug level\n\
\'level\' = [0|1|2]\n", getName());
printf("%s disk\n\
show all attached disks\n", getName());
} else if (!strcmp(argv[1], "debug")) {
if(argv[2]){
int level = atoi(argv[2]);
if (level != 0 && level != 1 && level != 2)
debug_err("%s: unsupported debug level <%s>\n", getName(), argv[2]);
else {
debug_level = level;
confSpace->set_debug_level(debug_level);
printf("%s: set debug level to %d\n", getName(), debug_level);
}
}else
printf("%s: current debug level %d\n", getName(), debug_level);
} else if (!strcmp(argv[1], "disk")) {
printf("attached disks:\n");
for (int i = 0; i < IOC_NUM_PORTS; i++)
if (_disk[i])
printf("%s is attached to target %d\n", _disk[i]->get_name(), i);
}
else {
debug_err("%s: unsupported UI command <%s>\n", getName(), argv[1]);
}
}
int sas_ui_cmd(void *obj, int argc, char * argv[]){
SAS *sas = dynamic_cast<SAS *>((Module *)obj);
sas->handle_ui_cmd(argc, argv);
return(0);
}
void SAS::init_done(){
// create disk overlay
if (!isDir(overlaydir)) {
int status = mkdir(overlaydir, 0777);
if (status != 0) {
debug_err("%s: Unable to create disk overlay dir: %s (%s)\n", getName(), overlaydir, strerror(errno));
exit(1);
}
// .nsr is created in overlay directory to avoid backup of shadow files
char *fname = (char *)malloc(strlen(overlaydir) + 6);
sprintf(fname, "%s/.nsr", overlaydir);
FILE *fp = fopen(fname, "w");
if (fp == NULL) {
debug_err("%s: Unable to creat .nsr in %s (%s)\n", getName(), overlaydir, strerror(errno));
exit(1);
}
fprintf(fp, "+null: * .?*\n");
fclose(fp);
free(fname);
status = atexit(overlaydir_cleanup);
if (status != 0) {
debug_err("%s: Unable to register cleanup function for overlay dir: %s (%s)\n", getName(), overlaydir, strerror(errno));
exit(1);
}
}
// attach disks
for (int i = 0; i < IOC_NUM_PORTS; i++)
_disk[i] = NULL;
FILE *fp = NULL;
// open disk configuration file, try the restore directory first
char *restore_dir = get_restore_dir();
if (restore_dir) {
// need to extract file name
int i;
for (i = strlen(fname) - 1; i >= 0; i--)
if (fname[i] == '/')
break;
char *name = strdup(&fname[i+1]);
// open it if it is in the restore directory
char *dev_name = (char *)getName();
char *rname = (char *)malloc(strlen(restore_dir) + strlen(dev_name) + strlen(name) + 15);
sprintf(rname, "%s/sas_ctrl_%s/%s", restore_dir, dev_name, name);
fp = fopen(rname, "r");
if (!fp)
debug_info("%s: can't find sas disk configuration file: %s in the restore directory\n", getName(), name);
else {
// update fname with rname
if (strlen(fname) < strlen(rname)) {
free((void *)fname);
fname = strdup(rname);
}
else
strcpy((char *)fname, rname);
}
free(name);
free(rname);
}
// open disk configuration file, try the one specified in .rc file next
if (!fp)
fp = fopen(fname, "r");
if (!fp) {
debug_err("%s: can't find sas disk configuration file: %s\n", getName(), fname);
exit(1);
}
for (int target_id = 0; target_id < IOC_NUM_PORTS; target_id++) {
SCSIDisk *disk = new SCSIDisk;
// disk name is constructed as controller name + "t" + tatget_id
char disk_name[64];
sprintf(disk_name, "%st%d", getName(), target_id);
disk->set_name(disk_name);
disk->set_target(target_id);
disk->parse_config(fname, fp, target_id);
if (disk->get_num_partitions() == 0) {
delete disk;
continue;
}
_disk[target_id] = disk;
debug_info("%s: disk %s is attached as target %d, total number of partitons: %d\n",
getName(), disk->get_name(), disk->get_target_id(), disk->get_num_partitions());
}
fclose(fp);
// register ui commands
mmi_register_instance_cmd(getInstance(), get_help(), sas_ui_cmd);
// initialize pci registers
initPci();
dev_init_done(Module::debug_level);
}
void SAS::module_added(mmi_instance_t target, const char *target_name) {
dev_module_added(target_name);
}
void SAS::module_deleted(mmi_instance_t target, const char *target_name) {
dev_module_deleted(target_name);
}
void SAS::modinfo() {
printf("%s is a %s\n", getName(), SAS1064E_Help);
printf(" Vendor id <0x%lx>, Device id <0x%lx>\n", confSpace->readConf(PCI_CONF_VENID), confSpace->readConf(PCI_CONF_DEVID));
printf(" Upstream pcie bus is <%s>\n", busName);
printf(" Disk configuration file is <%s>\n", fname);
printf(" Type \'%s\' for the description of supported UI commands\n", getName());
}
void *SAS::get_interface(const char *name) {
if (!strcmp(name, GENERIC_PCIE_DEV_INTERFACE))
return (genericPcieDevIf*)this;
else
return NULL;
}
void SAS::reset_ioc() {
// clear all registers
bzero(_mpt_reg, sizeof(mpt_reg_t));
// clear ioc configuration
bzero(_ioc_conf, sizeof(ioc_conf_t));
// clear earliest serving time
bzero(_earliest_serving_time, sizeof(uint64_t) * IOC_NUM_PORTS);
// diable ports
bzero(_port_enabled, sizeof(uint8_t) * IOC_NUM_PORTS);
// clear handshake messages
bzero(_hdshk_msg_req_buf, HDSHK_MSG_SIZE);
bzero(_hdshk_msg_reply_buf, HDSHK_MSG_SIZE);
_hdshk_msg_req_size = 0;
_hdshk_msg_req_recv = 0;
_hdshk_msg_reply_size = 0;
_hdshk_msg_reply_send = 0;
// clear queues
_request_queue_size = 0;
while (!_reply_post_queue.empty())
_reply_post_queue.pop();
while (!_reply_free_queue.empty())
_reply_free_queue.pop();
// clear event table
_etable.clear();
// set ioc to ready state
_mpt_reg->doorbell = (_mpt_reg->doorbell & ~MPI_IOC_STATE_MASK) |
MPI_IOC_STATE_READY;
// set interrupt masks
_mpt_reg->intr_mask |= MPI_HIM_DIM;
_mpt_reg->intr_mask |= MPI_HIM_RIM;
}
pcieCompleter SAS::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) {
bool ret;
int size = byteEnabletoSize(be); // length is the number of words
if(id)*id = samId;
if (wr) {
*(uint64_t*)data &= 0xFFFFFFFF;
uint32_t l_buf = *(uint64_t*)data;
if(size == 4){
*(uint64_t*)data = swap_word(l_buf);
}else if(size == 2){
*(uint64_t*)data = swap_hword(l_buf);
}else if (size == 1)
*(uint64_t*)data = l_buf;
debug_info("%s: conf xactn type 0: offset <0x%x> wr size<%d> be<0x%x> data<0x%llx>\n",
getName(), offset, size, be,*(uint64_t*)data);
}
ret = confSpace->confAccessSize(offset, wr, (uint64_t *)data, size);
if (wr) debug_info("\n");
if (!wr) {
uint32_t l_buf = *(uint64_t*)data;
if(size == 4){
*(uint64_t*)data = swap_word(l_buf & 0xFFFFFFFF);
}else if(size == 2){
*(uint64_t*)data = swap_hword(l_buf & 0xFFFF);
}else if (size == 1)
*(uint64_t*)data = l_buf & 0xFF;
debug_info("%s: conf xactn type 0: offset <0x%x> rd size<%d> be<0x%x> data<0x%llx>\n",
getName(), offset, size, be, *(uint64_t*)data);
debug_info("\n");
}
devif_confAccessCb(wr,offset,be);
return (ret ? pcieCompleter(SC,getId()) : pcieCompleter(CA,getId()));
}
void SAS::devif_confAccessCb(bool wr, uint64_t offset, uint8_t be) {
if(!wr)
return;
if(offset == PCI_CONF_BASE0){
_io_base = confSpace->readConf(offset) & 0xfffffffc;
}else if (offset == PCI_CONF_BASE1 || offset == PCI_CONF_BASE2){
pcieBaseAddrReg * r = (pcieBaseAddrReg*)confSpace->getConfReg(PCI_CONF_BASE1);
_mem0_base = ((uint64_t)(r->val_32To63)) << 32 |(r->getVal() & 0xfffffff0) ;
}else if (offset == PCI_CONF_BASE3 || offset == PCI_CONF_BASE4){
pcieBaseAddrReg * r = (pcieBaseAddrReg*)confSpace->getConfReg(PCI_CONF_BASE3);
_mem1_base = ((uint64_t)(r->val_32To63)) << 32 |(r->getVal() & 0xfffffff0) ;
}
}
//
bool SAS::msi_enabled() {
uint16_t msi_msg_ctrl = confSpace->readConf(PCI_CONF_MSI_CAP_ID + MSI_MSGCNTRL_OFFSET);
return(msi_msg_ctrl & 0x1);
}
// called whenever IOC writes a value to doorbell
void SAS::set_doorbell_interrupt() {
bool pending = _mpt_reg->intr_status & MPI_HIS_DOORBELL_INTERRUPT;
// set doorbell interrupt bit
_mpt_reg->intr_status |= MPI_HIS_DOORBELL_INTERRUPT;
// send interrupt if it is unmasked and no pending doorbell interrupt
if (!(_mpt_reg->intr_mask & MPI_HIM_DIM) && !pending) {
if (msi_enabled()) {
// send MSI
debug_info("%s: send doorbell MSI\n", getName());
uint32_t lo = confSpace->readConf(PCI_CONF_MSI_CAP_ID + MSI_MSGADDR0_OFFSET);
uint32_t hi = confSpace->readConf(PCI_CONF_MSI_CAP_ID + MSI_MSGADDR1_OFFSET);
uint64_t msi_addr = ((uint64_t)hi << 32) | lo;
uint32_t msi_data = confSpace->readConf(PCI_CONF_MSI_CAP_ID + MSI_MSGDATA64_OFFSET);
pcieCompleter status = busIf->busif_access(PCIE_MEM, true, msi_addr, (void*)&msi_data,
1, 0xf, getId(), mem_addr64, &samId);
if (handleCompletion(status) == -1) {
debug_err("%s: pcie error when sending doorbell MSI\n");
exit(1);
}
}
else {
// send INTx
debug_info("%s: send doorbell INTx\n", getName());
pcieCompleter status = busIf->busif_msgAccess((uint8_t)MSG_Assert_INTA, local, getId(),&samId);
if (handleCompletion(status) == -1) {
debug_err("%s: pcie error when sending doorbell INTx\n");
exit(1);
}
}
}
}
// called whenever IOC writes a reply msg descriptor to reply post queue
void SAS::set_reply_msg_interrupt() {
bool pending = _mpt_reg->intr_status & MPI_HIS_REPLY_MESSAGE_INTERRUPT;
// set reply msg interrupt bit
_mpt_reg->intr_status |= MPI_HIS_REPLY_MESSAGE_INTERRUPT;
// send interrupt if it is unmasked and no pending reply msg interrupt
if (!(_mpt_reg->intr_mask & MPI_HIM_RIM) && !pending) {
if (msi_enabled()) {
// send MSI
debug_info("%s: send reply msg MSI\n", getName());
uint32_t lo = confSpace->readConf(PCI_CONF_MSI_CAP_ID + MSI_MSGADDR0_OFFSET);
uint32_t hi = confSpace->readConf(PCI_CONF_MSI_CAP_ID + MSI_MSGADDR1_OFFSET);
uint64_t msi_addr = ((uint64_t)hi << 32) | lo;
uint32_t msi_data = confSpace->readConf(PCI_CONF_MSI_CAP_ID + MSI_MSGDATA64_OFFSET);
pcieCompleter status = busIf->busif_access(PCIE_MEM, true, msi_addr, (void*)&msi_data,
1, 0xf, getId(), mem_addr64, &samId);
if (handleCompletion(status) == -1) {
debug_err("%s: pcie error when sending reply msg MSI\n");
exit(1);
}
}
else {
// send INTx
debug_info("%s: send reply msg INTx\n", getName());
pcieCompleter status = busIf->busif_msgAccess((uint8_t)MSG_Assert_INTA, local, getId(),&samId);
if (handleCompletion(status) == -1) {
debug_err("%s: pcie error when sending reply msg INTx\n");
exit(1);
}
}
}
}
void SAS::doorbell_function_handshake(uint8_t size) {
// set by ioc to indicate it starts to perform a doorbell function
_mpt_reg->doorbell |= MPI_DOORBELL_USED;
// clear to indicate ioc has read the message
_mpt_reg->intr_status &= ~MPI_HIS_IOP_DOORBELL_STATUS;
// set the size of total and received handhake message (in dword)
bzero(_hdshk_msg_req_buf, HDSHK_MSG_SIZE);
assert(size > 0 && size <= HDSHK_MSG_SIZE >> 2);
_hdshk_msg_req_size = size;
_hdshk_msg_req_recv = 0;
// set by ioc after the doorbell is written
set_doorbell_interrupt();
}
void SAS::doorbell_function_reply_frame_removal() {
// set by ioc to indicate it starts to perform a doorbell function
_mpt_reg->doorbell |= MPI_DOORBELL_USED;
// clear to indicate ioc has read the message
_mpt_reg->intr_status &= ~MPI_HIS_IOP_DOORBELL_STATUS;
// use doorbell to send the reply message
*((uint32_t *)_hdshk_msg_reply_buf) = SAM_LE_32(_reply_free_queue.size());
_hdshk_msg_reply_size = 1;
while (!_reply_free_queue.empty()) {
*((uint32_t *)_hdshk_msg_reply_buf + _hdshk_msg_reply_size) = SAM_LE_32(_reply_free_queue.front());
_reply_free_queue.pop();
_hdshk_msg_reply_size++;
}
uint16_t value;
// set the size of message to be sent in hword
_hdshk_msg_reply_size = _hdshk_msg_reply_size << 1;
_hdshk_msg_reply_send = 0;
// put the first hword in the doorbell
value = *(uint16_t *)(_hdshk_msg_reply_buf + (_hdshk_msg_reply_send << 1));
value = SAM_LE_16(value);
_mpt_reg->doorbell = (_mpt_reg->doorbell & ~MPI_DOORBELL_DATA_MASK) | value;
_hdshk_msg_reply_send++;
// set by ioc after the doorbell is written
set_doorbell_interrupt();
}
void SAS::doorbell_function_ioc_msg_unit_reset() {
// set by ioc to indicate it starts to perform a doorbell function
_mpt_reg->doorbell |= MPI_DOORBELL_USED;
// clear to indicate ioc has read the message
_mpt_reg->intr_status &= ~MPI_HIS_IOP_DOORBELL_STATUS;
// clear queues
while (!_reply_post_queue.empty())
_reply_post_queue.pop();
while (!_reply_free_queue.empty())
_reply_free_queue.pop();
// clear event table
_etable.clear();
// set ioc to ready state
_mpt_reg->doorbell = (_mpt_reg->doorbell & ~MPI_IOC_STATE_MASK) |
MPI_IOC_STATE_READY;
// clear to indicate ioc finishes doorbell function
_mpt_reg->doorbell &= ~MPI_DOORBELL_USED;
}
void SAS::access_door_bell(bool wr, uint64_t *in_buf, uint8_t size) {
if (wr) {
uint32_t value = *in_buf;
// request messages received through doorbell handshake
// message is written in 4 bytes chunk using little endian model
if (_hdshk_msg_req_recv < _hdshk_msg_req_size) {
*((uint32_t *)(_hdshk_msg_req_buf + (_hdshk_msg_req_recv << 2))) = SAM_LE_32(value);
_mpt_reg->intr_status &= ~MPI_HIS_IOP_DOORBELL_STATUS;
_hdshk_msg_req_recv++;
if (_hdshk_msg_req_recv == _hdshk_msg_req_size) {
process_handshake_msg();
_hdshk_msg_req_size = 0;
_hdshk_msg_req_recv = 0;
}
return;
}
// doorbell function
uint32_t d_fun = (value & MPI_DOORBELL_FUNCTION_MASK) >> MPI_DOORBELL_FUNCTION_SHIFT;
switch (d_fun) {
case MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET:
doorbell_function_ioc_msg_unit_reset();
break;
case MPI_FUNCTION_IO_UNIT_RESET:
debug_err("%s: MPI_FUNCTION_IO_UNIT_RESET no impl\n", getName());
exit(1);
break;
case MPI_FUNCTION_HANDSHAKE:
doorbell_function_handshake((value & MPI_DOORBELL_ADD_DWORDS_MASK) >>
MPI_DOORBELL_ADD_DWORDS_SHIFT);
break;
case MPI_FUNCTION_REPLY_FRAME_REMOVAL:
doorbell_function_reply_frame_removal();
break;
case MPI_FUNCTION_HOST_PAGEBUF_ACCESS_CONTROL:
debug_err("%s: MPI_FUNCTION_HOST_PAGEBUF_ACCESS_CONTROL no impl\n", getName());
exit(1);
break;
default:
debug_err("%s: unknown doorbell function\n", getName());
exit(1);
break;
}
}
else {
*in_buf = _mpt_reg->doorbell;
}
}
void SAS::access_write_sequence(bool wr, uint64_t *in_buf, uint8_t size) {
uint32_t key_value = *in_buf & MPI_WRSEQ_KEY_VALUE_MASK;
if (!wr) {
debug_err("%s: write sequence register is write only!\n", getName());
return;
}
// A sequence of values have to be written before Host Diagnostic register is accessed.
// Any incorrect value will cause the restart of matching process.
switch (key_value) {
case MPI_WRSEQ_1ST_KEY_VALUE:
// disable host diag write if it is set
if (_mpt_reg->write_seq == MPI_WRSEQ_5TH_KEY_VALUE)
_mpt_reg->diag &= ~MPI_DIAG_DRWE;
// a new sequence starts
_mpt_reg->write_seq = key_value;
break;
case MPI_WRSEQ_2ND_KEY_VALUE:
// disable host diag write if it is set
if (_mpt_reg->write_seq == MPI_WRSEQ_5TH_KEY_VALUE)
_mpt_reg->diag &= ~MPI_DIAG_DRWE;
if (_mpt_reg->write_seq == MPI_WRSEQ_1ST_KEY_VALUE)
_mpt_reg->write_seq = key_value;
else {
_mpt_reg->write_seq = 0;
}
break;
case MPI_WRSEQ_3RD_KEY_VALUE:
// disable host diag write if it is set
if (_mpt_reg->write_seq == MPI_WRSEQ_5TH_KEY_VALUE)
_mpt_reg->diag &= ~MPI_DIAG_DRWE;
if (_mpt_reg->write_seq == MPI_WRSEQ_2ND_KEY_VALUE)
_mpt_reg->write_seq = key_value;
else {
_mpt_reg->write_seq = 0;
}
break;
case MPI_WRSEQ_4TH_KEY_VALUE:
// disable host diag write if it is set
if (_mpt_reg->write_seq == MPI_WRSEQ_5TH_KEY_VALUE)
_mpt_reg->diag &= ~MPI_DIAG_DRWE;
if (_mpt_reg->write_seq == MPI_WRSEQ_3RD_KEY_VALUE)
_mpt_reg->write_seq = key_value;
else {
_mpt_reg->write_seq = 0;
}
break;
case MPI_WRSEQ_5TH_KEY_VALUE:
// disable host diag write if it is set
if (_mpt_reg->write_seq == MPI_WRSEQ_5TH_KEY_VALUE)
_mpt_reg->diag &= ~MPI_DIAG_DRWE;
if (_mpt_reg->write_seq == MPI_WRSEQ_4TH_KEY_VALUE)
_mpt_reg->write_seq = key_value;
else {
_mpt_reg->write_seq = 0;
}
// enable host diag write
if (_mpt_reg->write_seq == MPI_WRSEQ_5TH_KEY_VALUE)
_mpt_reg->diag |= MPI_DIAG_DRWE;
break;
default:
_mpt_reg->write_seq = 0;
_mpt_reg->diag &= ~MPI_DIAG_DRWE;
break;
}
}
void SAS::access_host_diagnostic(bool wr, uint64_t *in_buf, uint8_t size) {
if (wr) {
uint32_t value = *in_buf & 0x7ff;
_mpt_reg->diag = value;
if (value & MPI_DIAG_RESET_ADAPTER) {
reset_ioc();
_mpt_reg->diag &= ~MPI_DIAG_RESET_ADAPTER;
}
}
else {
*in_buf = _mpt_reg->diag;
}
}
void SAS::access_test_base_address(bool wr, uint64_t *in_buf, uint8_t size) {
if (wr) {
_mpt_reg->test_base_addr = *in_buf;
}
else {
*in_buf = _mpt_reg->test_base_addr;
}
}
void SAS::access_diag_rw_data(bool wr, uint64_t *in_buf, uint8_t size) {
if (wr) {
_mpt_reg->diagrw_data = *in_buf;
}
else {
*in_buf = _mpt_reg->diagrw_data;
}
}
void SAS::access_diag_rw_address(bool wr, uint64_t *in_buf, uint8_t size) {
if (wr) {
_mpt_reg->diagrw_addr = *in_buf;
}
else {
*in_buf = _mpt_reg->diagrw_addr;
}
}
void SAS::access_host_interrupt_status(bool wr, uint64_t *in_buf, uint8_t size) {
if (wr) {
uint32_t l_buf = *in_buf;
if (!msi_enabled()) {
// deassert interrupt
if (_mpt_reg->intr_status & MPI_HIS_DOORBELL_INTERRUPT) {
// don't deassert interrupt if reply msg interrupt is set
if ((_mpt_reg->intr_status & MPI_HIS_REPLY_MESSAGE_INTERRUPT) == 0) {
debug_info("%s: deassert interrupt\n", getName());
pcieCompleter status = busIf->busif_msgAccess((uint8_t)MSG_Deassert_INTA, local, getId(),&samId);
if (handleCompletion(status) == -1) {
debug_err("%s: pcie error when deasserting doorbell interrupt\n");
exit(1);
}
}
}
}
// reply msg interrupt is not cleared by ANY write
_mpt_reg->intr_status = l_buf | (_mpt_reg->intr_status & MPI_HIS_REPLY_MESSAGE_INTERRUPT);
// doorbell interrupt is always cleared by ANY write
_mpt_reg->intr_status &= ~MPI_HIS_DOORBELL_INTERRUPT;
// reply messages sent through doorbell handshake
if ((_mpt_reg->intr_status & MPI_HIS_DOORBELL_INTERRUPT) == 0) {
if (_hdshk_msg_reply_send < _hdshk_msg_reply_size) {
uint32_t value = SAM_LE_16(*(uint16_t *)(_hdshk_msg_reply_buf + (_hdshk_msg_reply_send << 1)));
_mpt_reg->doorbell = (_mpt_reg->doorbell & ~MPI_DOORBELL_DATA_MASK) | value;
_hdshk_msg_reply_send++;
// set by ioc after the doorbell is written
set_doorbell_interrupt();
}
else if (_hdshk_msg_reply_send > 0 && _hdshk_msg_reply_send == _hdshk_msg_reply_size) {
_hdshk_msg_reply_size = 0;
_hdshk_msg_reply_send = 0;
// clear to indicate ioc finishes doorbell function
_mpt_reg->doorbell &= ~MPI_DOORBELL_USED;
// set by ioc after the doorbell is written
set_doorbell_interrupt();
}
}
}
else {
*in_buf = _mpt_reg->intr_status;
}
}
void SAS::access_host_interrupt_mask(bool wr, uint64_t *in_buf, uint8_t size) {
if (wr) {
// In some very rare cases, target probe starts (which masks the interrupt)
// while ioc is still processing a scsi cmd. We avoid mask in such cases.
bool pending_cmd = false;
for (int i = 0; i < EVENT_TABLE_SIZE; i++) {
if (_etable.events[i].used) {
pending_cmd = true;
break;
}
}
if (pending_cmd) {
debug_info("%s: ioc is busy, reply msg interrupt will not be masked \n", getName());
_mpt_reg->intr_mask = (_mpt_reg->intr_mask & MPI_HIM_RIM) | (*in_buf & ~MPI_HIM_RIM);
}
else {
_mpt_reg->intr_mask = *in_buf;
}
}
else {
*in_buf = _mpt_reg->intr_mask;
}
}
void SAS::access_host_request_queue(bool wr, uint64_t *in_buf, uint8_t size) {
if (wr) {
uint32_t l_buf = *in_buf;
// request is processed immediately after it is received,
// so request post queue is not needed.
_request_queue_size++;
// timing model is done through event table
int entry = _etable.alloc();
// process io request
process_mpi_msg(entry, l_buf);
int64_t invoke_time = _etable.get_invoke_time(entry);
int64_t current_time = mmi_get_time();
debug_info("%s: io request is received, current time is %lld\n", getName(), current_time);
if (invoke_time == current_time) {
// no disk delay is simulated, finish it immediately
sas_io_callback((void *)this, (void*)entry);
} else if (invoke_time > current_time) {
// register callback function since we now know when an io request will finish
mmi_register_event(_etable.get_invoke_time(entry), (EventFunc_T*)&sas_io_callback, (void *)this, (void *)entry);
} else {
// invoke time should not be less than current time
debug_err("%s: callback function invoke time %lld is less than current time %lld\n", getName(), invoke_time, current_time);
exit(1);
}
_request_queue_size--;
}
else {
*in_buf = _mpt_reg->req_q;
}
}
void SAS::access_reply_queue(bool wr, uint64_t *in_buf, uint8_t size) {
if (wr) {
uint32_t l_buf = *in_buf;
// each written value is regarded as reply free MFA from the host
// which will be pushed into reply free queue
_reply_free_queue.push(l_buf);
}
else {
// without disk delay, reading reply queue and updating reply
// queue are mutually exclusive (protected by lock in mpt driver).
// if disk delay is simulated, this is no longer true, because
// updating reply queue is done some time later after a request
// is received, and it is no longer under the proctection of mpt driver.
pthread_mutex_lock(&_reply_queue_mutex);
if (!_reply_post_queue.empty()) {
assert(_mpt_reg->intr_status & MPI_HIS_REPLY_MESSAGE_INTERRUPT);
_mpt_reg->reply_q = _reply_post_queue.front();
_reply_post_queue.pop();
if (_reply_post_queue.empty()) {
// self-clear reply message interrupt if queue is empty
_mpt_reg->intr_status &= ~MPI_HIS_REPLY_MESSAGE_INTERRUPT;
if (!msi_enabled()) {
// don't deassert interrupt if doorbell interrupt is set
if ((_mpt_reg->intr_status & MPI_HIS_DOORBELL_INTERRUPT) == 0) {
debug_info("%s: deassert interrupt\n", getName());
pcieCompleter status = busIf->busif_msgAccess((uint8_t)MSG_Deassert_INTA, local, getId(),&samId);
if (handleCompletion(status) == -1) {
debug_err("%s: pcie error when deasserting reply msg interrupt\n");
exit(1);
}
}
}
}
}
else {
// interrupt should have been cleared when the last message is read
assert(!(_mpt_reg->intr_status & MPI_HIS_REPLY_MESSAGE_INTERRUPT));
// send 0xffffffff to the host indicating reply post queue is empty
_mpt_reg->reply_q = 0xffffffff;
_mpt_reg->intr_status &= ~MPI_HIS_REPLY_MESSAGE_INTERRUPT;
}
pthread_mutex_unlock(&_reply_queue_mutex);
*in_buf = _mpt_reg->reply_q;
}
}
void SAS::access_hi_pri_request_queue(bool wr, uint64_t *in_buf, uint8_t size) {
if (wr) {
_mpt_reg->pri_req_q = *in_buf;
}
else {
*in_buf = _mpt_reg->pri_req_q;
}
}
bool SAS::memory_space_access(int offset,bool wr, uint64_t *in_buf, uint8_t size) {
// must be a word
assert(size == 4);
assert(offset >= 0);
if (offset >= _io_base && offset < _io_base + IO_SIZE) {
offset -= _io_base;
}
else if (offset >= _mem0_base && offset < _mem0_base + MEM0_SIZE) {
offset -= _mem0_base;
}
else if (offset >= _mem1_base && offset < _mem1_base + MEM1_SIZE) {
offset -= _mem1_base;
}
else {
debug_err("%s: memory space 0x%llx is not allocated\n", getName(), offset);
return false;
}
switch (offset) {
case MPI_DOORBELL_OFFSET:
access_door_bell(wr, in_buf, size);
break;
case MPI_WRITE_SEQUENCE_OFFSET:
access_write_sequence(wr, in_buf, size);
break;
case MPI_DIAGNOSTIC_OFFSET:
access_host_diagnostic(wr, in_buf, size);
break;
case MPI_TEST_BASE_ADDRESS_OFFSET:
access_test_base_address(wr, in_buf, size);
break;
case MPI_DIAG_RW_DATA_OFFSET:
access_diag_rw_data(wr, in_buf, size);
break;
case MPI_DIAG_RW_ADDRESS_OFFSET:
access_diag_rw_address(wr, in_buf, size);
break;
case MPI_HOST_INTERRUPT_STATUS_OFFSET:
access_host_interrupt_status(wr, in_buf, size);
break;
case MPI_HOST_INTERRUPT_MASK_OFFSET:
access_host_interrupt_mask(wr, in_buf, size);
break;
case MPI_REQUEST_QUEUE_OFFSET:
access_host_request_queue(wr, in_buf, size);
break;
case MPI_REPLY_QUEUE_OFFSET:
access_reply_queue(wr, in_buf, size);
break;
case MPI_HI_PRI_REQUEST_QUEUE_OFFSET:
access_hi_pri_request_queue(wr, in_buf, size);
break;
default:
debug_err("%s: memory space 0x%llx is reserved\n", getName(), offset);
return false;
}
return true;
}
pcieCompleter SAS::devif_memAccess(bool wr, uint64_t offset, addrMd_xactnType mode, void * data,\
uint16_t length, uint8_t be, uint16_t reqId, tlp_X args,SAM_DeviceId *id) {
bool ret;
int size = byteEnabletoSize(be);
if(id) *id = samId;
if (wr) {
*(uint64_t*)data &= 0xFFFFFFFF;
uint32_t l_buf = *(uint64_t*)data;
if(size == 4){
*(uint64_t*)data = swap_word(l_buf);
}else if(size == 2){
*(uint64_t*)data = swap_hword(l_buf);
}else if (size == 1)
*(uint64_t*)data = l_buf;
debug_info("%s: memory space: offset <0x%x> wr size<%d> be<0x%x> data<0x%llx>\n",
getName(), offset, size, be, *(uint64_t*)data);
}
ret = memory_space_access(offset, wr, (uint64_t *)data, size);
if (!wr) {
uint32_t l_buf = *(uint64_t*)data;
if(size == 4){
*(uint64_t*)data = swap_word(l_buf & 0xFFFFFFFF);
}else if(size == 2){
*(uint64_t*)data = swap_hword(l_buf & 0xFFFF);
}else if (size == 1)
*(uint64_t*)data = l_buf & 0xFF;
debug_info("%s: memory space: offset <0x%x> rd size<%d> be<0x%x> data<0x%llx>\n",
getName(), offset, size, be, *(uint64_t*)data);
debug_info("\n");
}
return (ret ? pcieCompleter(SC,getId()) : pcieCompleter(UR,getId()));
}
pcieCompleter SAS::devif_ioAccess(bool wr, uint32_t offset, void * data, uint8_t be, \
uint16_t reqId, uint16_t length, tlp_X args,SAM_DeviceId* id) {
debug_info("%s: io space 0x%llx is %s\n", getName(), offset, wr?"written":"read");
if(id) *id = samId;
return pcieCompleter();
}
pcieCompleter SAS::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) {
debug_info("%s: message space is accessed\n", getName());
if(id) *id = samId;
return pcieCompleter();
}
const char *Module::get_help_string(){
return SAS1064E_Help;
}
Module *Module::create(const char *_modname, const char *_instance_name){
return new SAS(_modname, _instance_name);
}
bool SAS::dump(FILE *fp) {
const char *dump_dir = DR_get_dir();
const char *dev_name = getName();
uint16_t qsize;
uint32_t qvalue;
int i;
assert(dump_dir);
// dump generic pcie device info into "dump_dir/dev_name.pcie"
bool ret = genericPcieDev::dump(dump_dir, dev_name);
// dump all disk info are dumped into "dump_dir/sas_ctrl_dev_name/"
char *dirname = (char *)malloc(strlen(dump_dir) + strlen(dev_name) + 15);
sprintf(dirname, "%s/sas_ctrl_%s", dump_dir, dev_name);
if (mkdir(dirname, S_IRWXU | S_IRWXG | S_IRWXO) == -1) {
debug_err("%s: can't create directory %s\n", getName(), dirname);
}
for (i = 0; i < IOC_NUM_PORTS; i++)
if (_disk[i])
_disk[i]->dump(dirname);
// dump all controller info into "dump_dir/dev_name.dmp"
// there are several categories, each categories starts with "#category_name number_of_fields"
// dump registers
fprintf(fp, "#reg %d\n", 11);
fprintf(fp, "reg_doorbell %u\n", _mpt_reg->doorbell);
fprintf(fp, "reg_write_seq %u\n", _mpt_reg->write_seq);
fprintf(fp, "reg_diag %u\n", _mpt_reg->diag);
fprintf(fp, "reg_test_base_addr %u\n", _mpt_reg->test_base_addr);
fprintf(fp, "reg_diagrw_data %u\n", _mpt_reg->diagrw_data);
fprintf(fp, "reg_diagrw_addr %u\n", _mpt_reg->diagrw_addr);
fprintf(fp, "reg_intr_status %u\n", _mpt_reg->intr_status);
fprintf(fp, "reg_intr_mask %u\n", _mpt_reg->intr_mask);
fprintf(fp, "reg_req_q %u\n", _mpt_reg->req_q);
fprintf(fp, "reg_reply_q %u\n", _mpt_reg->reply_q);
fprintf(fp, "reg_pri_req_q %u\n", _mpt_reg->pri_req_q);
// dump ioc configuration
fprintf(fp, "#conf %d\n", 9);
fprintf(fp, "conf_who_init %hhu\n", _ioc_conf->who_init);
fprintf(fp, "conf_max_devices %hhu\n", _ioc_conf->max_devices);
fprintf(fp, "conf_max_buses %hhu\n", _ioc_conf->max_buses);
fprintf(fp, "conf_reply_frame_size %hu\n", _ioc_conf->reply_frame_size);
fprintf(fp, "conf_host_mfa_high_addr %u\n", _ioc_conf->host_mfa_high_addr);
fprintf(fp, "conf_sense_buffer_high_addr %u\n", _ioc_conf->sense_buffer_high_addr);
fprintf(fp, "conf_host_page_buffer_sge_FlagsLength %u\n", _ioc_conf->host_page_buffer_sge.FlagsLength);
fprintf(fp, "conf_host_page_buffer_sge_Address_Low %u\n", _ioc_conf->host_page_buffer_sge.Address_Low);
fprintf(fp, "conf_host_page_buffer_sge_Address_High %u\n", _ioc_conf->host_page_buffer_sge.Address_High);
// dump eariliest serving time
fprintf(fp, "#port_est %d\n", IOC_NUM_PORTS);
for (i = 0; i < IOC_NUM_PORTS; i++)
fprintf(fp, "port[%d] %lld\n", i, _earliest_serving_time[i]);
// dump port enabled
fprintf(fp, "#port_enabled %d\n", IOC_NUM_PORTS);
for (i = 0; i < IOC_NUM_PORTS; i++)
fprintf(fp, "port[%d] %hhu\n", i, _port_enabled[i]);
// dump handshake request messages
fprintf(fp, "#hdshk_msg_req %d\n", _hdshk_msg_req_size+2);
fprintf(fp, "hdshk_msg_req_size %hhu\n", _hdshk_msg_req_size);
fprintf(fp, "hdshk_msg_req_recv %hhu\n", _hdshk_msg_req_recv);
for (i = 0; i < (_hdshk_msg_req_size << 2); i++)
fprintf(fp, "hdshk_msg_req_buf[%d], %hhu\n", i, _hdshk_msg_req_buf[i]);
// dump handshake reply messages
fprintf(fp, "#hdshk_msg_reply %d\n", _hdshk_msg_reply_size+2);
fprintf(fp, "hdshk_msg_reply_size %hhu\n", _hdshk_msg_reply_size);
fprintf(fp, "hdshk_msg_reply_send %hhu\n", _hdshk_msg_reply_send);
for (i = 0; i < (_hdshk_msg_reply_size << 2); i++)
fprintf(fp, "hdshk_msg_reply_buf[%d], %hhu\n", i, _hdshk_msg_reply_buf[i]);
// dump request queue
fprintf(fp, "#request_queue %d\n", _request_queue_size);
// dump reply post queue
qsize = _reply_post_queue.size();
fprintf(fp, "#reply_post_queue %d\n", qsize);
for (i = 0; i < qsize; i++) {
qvalue = _reply_post_queue.front();
fprintf(fp, "reply_post_queue[%d] %u\n", i, qvalue);
_reply_post_queue.pop();
_reply_post_queue.push(qvalue);
}
// dump reply free queue
qsize = _reply_free_queue.size();
fprintf(fp, "#reply_free_queue %d\n", qsize);
for (i = 0; i < qsize; i++) {
qvalue = _reply_free_queue.front();
fprintf(fp, "reply_free_queue[%d] %u\n", i, qvalue);
_reply_free_queue.pop();
_reply_free_queue.push(qvalue);
}
// dump event table
fprintf(fp, "#event_table %d\n", EVENT_TABLE_SIZE);
_etable.dump(this, fp);
// copy disk configuration file into "dump_dir/sas_ctrl_dev_name/"
char *cpcmd = (char *)malloc(strlen(fname) + strlen(dirname) + 10);
sprintf(cpcmd, "cp %s %s", fname, dirname);
if (system(cpcmd) != 0) {
debug_err("%s: can't dump disk configuration file %s to %s\n", getName(), fname, dirname);
exit(1);
}
free(dirname);
free(cpcmd);
return ret;
}
bool SAS::restore(FILE * fp) {
const char *dump_dir = DR_get_dir();
const char *dev_name = getName();
char name[128], type[128];
uint32_t items;
uint32_t qvalue;
int i, num_ports;
// restore generic pcie device information from "dump_dir/dev_name.pcie"
assert(dump_dir);
bool ret = genericPcieDev::restore(dump_dir, dev_name);
// restore disks from "dump_dir/sas_ctrl_dev_name/"
char *dirname = (char *)malloc(strlen(dump_dir) + strlen(dev_name) + 15);
sprintf(dirname, "%s/sas_ctrl_%s", dump_dir, dev_name);
for (i = 0; i < IOC_NUM_PORTS; i++)
if (_disk[i])
_disk[i]->restore(dirname);
// restore controller info from "dump_dir/dev_name.dmp".
// Data structure may be changed, so does checkpoint.
// We want to have compatibility among checkpoints,
// so a fully associative search/comparison is needed
// during restore. To make it efficient, info are organized
// into categories, so that we can afford fully associative
// search within each category, while each category itself
// is identified also by fully associative search.
// Some category may not need any search during restore.
while (fscanf(fp, "%s %d\n", &type, &items) != EOF) {
if (strcmp(type, "#reg") == NULL) {
// restore registers
uint32_t reg_value;
for (i = 0; i < items; i++) {
fscanf(fp, "%s %u\n", name, &reg_value);
if (strcmp(name, "reg_doorbell") == NULL)
_mpt_reg->doorbell = reg_value;
else if (strcmp(name, "reg_write_seq") == NULL)
_mpt_reg->write_seq = reg_value;
else if (strcmp(name, "reg_diag") == NULL)
_mpt_reg->diag = reg_value;
else if (strcmp(name, "reg_test_base_addr") == NULL)
_mpt_reg->test_base_addr = reg_value;
else if (strcmp(name, "reg_diagrw_data") == NULL)
_mpt_reg->diagrw_data = reg_value;
else if (strcmp(name, "reg_diagrw_addr") == NULL)
_mpt_reg->diagrw_addr = reg_value;
else if (strcmp(name, "reg_intr_status") == NULL)
_mpt_reg->intr_status = reg_value;
else if (strcmp(name, "reg_intr_mask") == NULL)
_mpt_reg->intr_mask = reg_value;
else if (strcmp(name, "reg_req_q") == NULL)
_mpt_reg->req_q = reg_value;
else if (strcmp(name, "reg_reply_q") == NULL)
_mpt_reg->reply_q = reg_value;
else if (strcmp(name, "reg_pri_req_q") == NULL)
_mpt_reg->pri_req_q = reg_value;
else
debug_info("%s: unknown register %s\n", getName(), name);
}
}
else if (strcmp(type, "#conf") == NULL) {
// restore ioc configuration
uint32_t conf_value;
for (i = 0; i < items; i++) {
fscanf(fp, "%s %u\n", name, &conf_value);
if (strcmp(name, "conf_who_init") == NULL)
_ioc_conf->who_init = conf_value;
else if (strcmp(name, "conf_max_devices") == NULL)
_ioc_conf->max_devices = conf_value;
else if (strcmp(name, "conf_max_buses") == NULL)
_ioc_conf->max_buses = conf_value;
else if (strcmp(name, "conf_reply_frame_size") == NULL)
_ioc_conf->reply_frame_size = conf_value;
else if (strcmp(name, "conf_host_mfa_high_addr") == NULL)
_ioc_conf->host_mfa_high_addr = conf_value;
else if (strcmp(name, "conf_sense_buffer_high_addr") == NULL)
_ioc_conf->sense_buffer_high_addr = conf_value;
else if (strcmp(name, "conf_host_page_buffer_sge_FlagsLength") == NULL)
_ioc_conf->host_page_buffer_sge.FlagsLength = conf_value;
else if (strcmp(name, "conf_host_page_buffer_sge_Address_Low") == NULL)
_ioc_conf->host_page_buffer_sge.Address_Low = conf_value;
else if (strcmp(name, "conf_host_page_buffer_sge_Address_High") == NULL)
_ioc_conf->host_page_buffer_sge.Address_High = conf_value;
}
}
else if (strcmp(type, "#port_est") == NULL) {
// restore eariliest serving time
for (i = 0; i < items; i++)
fscanf(fp, "%s %lld\n", name, &_earliest_serving_time[i]);
}
else if (strcmp(type, "#port_enabled") == NULL) {
// restore port enabled
for (i = 0; i < items; i++)
fscanf(fp, "%s %hhu\n", name, &_port_enabled[i]);
}
else if (strcmp(type, "#hdshk_msg_req") == NULL) {
// restore handshake request messages
fscanf(fp, "%s %hhu\n", name, &_hdshk_msg_req_size);
fscanf(fp, "%s %hhu\n", name, &_hdshk_msg_req_recv);
for (i = 0; i < (_hdshk_msg_req_size << 2); i++)
fscanf(fp, "%s %hhu\n", name, &_hdshk_msg_req_buf[i]);
}
else if (strcmp(type, "#hdshk_msg_reply") == NULL) {
// restore handshake reply messages
fscanf(fp, "%s %hhu\n", name, &_hdshk_msg_reply_size);
fscanf(fp, "%s %hhu\n", name, &_hdshk_msg_reply_send);
for (i = 0; i < (_hdshk_msg_reply_size << 2); i++)
fscanf(fp, "%s %hhu\n", name, &_hdshk_msg_reply_buf[i]);
}
else if (strcmp(type, "#request_queue") == NULL) {
// restore request queue
_request_queue_size = items;
}
else if (strcmp(type, "#reply_post_queue") == NULL) {
// restore reply post queue
for (i = 0; i < items; i++) {
fscanf(fp, "%s %u\n", name, &qvalue);
_reply_post_queue.push(qvalue);
}
}
else if (strcmp(type, "#reply_free_queue") == NULL) {
// restore reply free queue
for (i = 0; i < items; i++) {
fscanf(fp, "%s %u\n", name, &qvalue);
_reply_free_queue.push(qvalue);
}
}
else if (strcmp(type, "#event_table") == NULL) {
// restore event table
_etable.restore(this, fp);
}
else
debug_info("%s: unknown type %s\n", getName(), type);
}
free(dirname);
return ret;
}