/* * pcie_template_device.c * * Copyright (c) 2010 Adnan Khaleel * Cray Inc * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This 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 program; if not, see . */ /* This is essentially an example for a device that utilises PCIe and MSI-X */ #include "hw.h" #include "pc.h" #include "pci_ids.h" #include "msi.h" #include "msix.h" #include "pcie.h" #include "pcie_msix_template.h" #include "pcie_pkt.h" extern int pcie_server_fd; extern struct hostent *pcie_server; extern uint64 pcie_transId; extern bool pcie_server_connected; #define BYTE 1 #define WORD 2*BYTE #define LWORD 4*BYTE #define DWORD 8*BYTE /* Debug code */ #define DEBUG_PCIE_MSIX_TEMPLATE #ifdef DEBUG_PCIE_MSIX_TEMPLATE #define SPLIT64(x) (uint32_t)((x >> 32) & 0xffffffff), (uint32_t)(x & 0xffffffff) #define PRINT_DEBUG(format, ...) printf(format, __VA_ARGS__); #define PRINT_DEBUG0(format) printf(format); #else #define SPLIT64(x) #define PRINT_DEBUG(format, ...) #define PRINT_DEBUG0(format) #endif #define DEFINE_DEVICE_PROPERTIES(_state, _conf) #define PCIE_MSIX_VID PCI_VENDOR_ID_CRAY #define PCIE_MSIX_DID PCI_DEVICE_ID_CRAY_ASIC #define PCIE_MSIX_SS_DID 0x100 #define PCIE_MSIX_VERSION 0x1 #define PCIE_MSIX_MSI_SUPPORTED_FLAGS PCI_MSI_FLAGS_64BIT #define PCIE_MSIX_MSI_NR_VECTOR 1 #define PCIE_MSIX_MSI_OFFSET 0x70 #define PCIE_MSIX_SSVID_OFFSET 0x80 #define PCIE_MSIX_SVID 0 #define PCIE_MSIX_SSID 0 #define PCIE_MSIX_EXP_OFFSET 0x90 #define PCIE_MSIX_AER_OFFSET 0x100 /* PCI BUS commands */ #define CMD_IOREAD 0x02 #define CMD_IOWRITE 0x03 #define CMD_MEMREAD 0x06 #define CMD_MEMWRITE 0x07 #define CMD_CONFIGREAD 0x0A #define CMD_CONFIGWRITE 0x0B // PCI device header uint32_t g_cfg_init[0x100] = { // index : offset 0x030117db, // 0x00 : 0x00: Vendor ID and device ID 0x00100007, // 0x01 : 0x04: Command and status 0x00000010, // 0x02 : 0x08: Revision ID and class code 0x00800008, // 0x03 : 0x0c: BIST, header type, latency time and cache size 0x0000000c, // 0x04 : 0x10: BAR 0 0x00000020, // 0x05 : 0x14: BAR 1 0x00000000, // 0x06 : 0x18: BAR 2 0x00000000, // 0x07 : 0x1c: BAR 3 0x00000000, // 0x08 : 0x20: BAR 4 0x00000000, // 0x09 : 0x24: BAR 5 0x00000000, // 0x0a : 0x28 0x000017db, // 0x0b : 0x2c: Subsystem vendor ID and subsystem ID 0x00000000, // 0x0c : 0x30: Expansion ROM base address 0x00000080, // 0x0d : 0x34: Capabilities pointer 0x00000000, // 0x0e : 0x38 0x0000010b, // 0x0f : 0x3c: Interrupt line and interrupt pin 0, 0, 0, 0, // 0x10 : 0x40 0, 0, 0, 0, // 0x14 : 0x50 0, 0, 0, 0, // 0x18 : 0x60 0, 0, 0, 0, // 0x1c : 0x70 0x0003b001, // 0x20 : 0x80: Power management capabilities 0x00000008, // 0x21 : 0x84: Power management control/status 0x00000000, // 0x22 : 0x88 0x00000000, // 0x23 : 0x8c 0, 0, 0, 0, // 0x24 : 0x90 0, 0, 0, 0, // 0x28 : 0xa0 0x003fc011, // 0x2c : 0xb0: MSI-X control 0x00402000, // 0x2d : 0xb4: MSI-X table offset 0x00403000, // 0x2e : 0xb8: MSI-X pending interrupt 0x00000000, // 0x2f : 0xbc 0x00010010, // 0x30 : 0xc0: PCIe capability list register 0x00008120, // 0x31 : 0xc4: PCIe device capabilities 0x00092100, // 0x32 : 0xc8: PCIe device control and status 0x00004411, // 0x33 : 0xcc: Link capabilities 0x00110000, // 0x34 : 0xd0: Link control and status 0x00000000, // 0x35 : 0xd4 0x00000000, // 0x36 : 0xd8 0x00000000, // 0x37 : 0xdc 0x00000000, // 0x38 : 0xe0 0x00000012, // 0x39 : 0xe4: PCIe device capabilities 2 0x00000000, // 0x3a : 0xe8: PCIe device control and status 2 0x00000000, // 0x3b : 0xec: Link capabilities 2 0x00000000, // 0x3c : 0xf0: Link control and status 2 }; static const unsigned long long BAR_Regions[6][2] = { // len , type 0x2000000000ull { 0x2000000000ull, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64 | PCI_BASE_ADDRESS_MEM_PREFETCH} , //BAR0, { 0, 0} , // BAR1 { 0, 0} , // BAR2, { 0, 0} , // BAR3 { 0, 0} , // BAR4 { 0, 0} , // BAR5 }; #define TIMEOUT_SEC 0 #define TIMEOUT_USEC 50 extern int do_remote_pcie_model_socket_read(void* pcie_trans, int size, int to_sec, int to_usec, bool startup, int64_t loop_count); extern int do_remote_pcie_model_socket_write(void* pcie_trans, int size, int to_sec, int to_usec, bool startup, int64_t loop_count); typedef struct PCIE_MSIX_DEVState_St { PCIDevice dev; int mmio_index; uint32_t intrmask; uint32_t intrstatus; uint32_t doorbell; uint32_t vectors; uint32_t features; } PCIE_MSIX_DEVState; static uint64_t do_socket_transaction(void *opaque, bool rd, uint64_t addr, uint64_t val, int len) { int bar_region = 0, i, err; uint32_t reg_base, reg_size, base_addr; PCIDevice *d = opaque; PCIe_cmd_t pcie_trans; reg_base = d->dev.io_regions[bar_region].addr; reg_size = d->dev.io_regions[bar_region].size; base_addr = d->dev.io_regions[bar_region].addr; pcie_trans.pktType = (rd) ? CFG_RD_0 : CFG_WR_0; pcie_trans.addr64 = addr; pcie_trans.transId = pcie_transId++; for (i = 0; i < len; i++) pcie_trans.data[i] = (rd) ? 0 : ((val >> (i*8)) & 0xff); pcie_trans.data_len = len; pcie_trans.stop_server = false; pcie_trans.start_delta = 0; pcie_trans.isAlive = false; pcie_trans.valid = true; pcie_trans.aries_req = false; if (rd) { PRINT_DEBUG("%s: Cfg_Rd Addr=0x%08x%08x ReqId=%lld bar=%d\n", __FUNCTION__, SPLIT64(addr64), pcie_trans.transId, bar_region); } else { PRINT_DEBUG("%s: Cfg_Wr Addr=0x%08x%08x ReqId=%lld bar=%d Data=0x", __FUNCTION__, SPLIT64(addr64), pcie_trans.transId, bar_region); for (i = 0; i < len; i++) PRINT_DEBUG("%02x", pcie_trans.data[i]); PRINT_DEBUG0("\n"); } if (pcie_server_connected) { err = do_remote_pcie_model_socket_write((void*)&pcie_trans, sizeof(PCIe_cmd_t), TIMEOUT_SEC, TIMEOUT_USEC, false, 0); if (err > 0) { if (rd) { PCIe_cmd_t cmpltd_cmd; cmpltd_cmd.valid = false; err = do_remote_pcie_model_socket_read((void*)&cmpltd_cmd, sizeof(PCIe_cmd_t), TIMEOUT_SEC, TIMEOUT_USEC, false, 0); if ((err > 0) && (cmpltd_cmd.valid)) { if ((cmpltd_cmd.pktType == CMPLT_D) && (pcie_trans.transId == cmpltd_cmd.transId)) { val = cmpltd_cmd.data[3] << 24 | cmpltd_cmd.data[2] << 16 | cmpltd_cmd.data[1] << 8 | cmpltd_cmd.data[0]; PRINT_DEBUG("\n%s: Cfg_Rd Cmplt_D %d bytes. Addr=0x%08x%08x val=0x%08x%08x bar=%d",__FUNCTION__, len, SPLIT64(addr), SPLIT64(val), bar_region); } } } } } return val; } static void pcie_msix_notify(PCIDevice *d, uint16_t vector, bool trigger) { PRINT_DEBUG("\n%s: notify vector %d trigger:%d \n", __FUNCTION__, vector, trigger); if (msix_enabled(d)) { if (trigger) { msix_notify(d, vector); } } else if (msi_enabled(d)) { if (trigger){ msi_notify(d, vector); } } } static void pcie_msix_mem_write(void *opaque, uint32_t addr, uint32_t val, int len) { uint32_t reg_base, reg_size; uint32_t base_addr; int bar_region = 0; // Aries only has BAR 0/1 since its 64bit PCIE_MSIX_DEVState *d = opaque; bool write = false; reg_base = d->dev.io_regions[bar_region].addr; reg_size = d->dev.io_regions[bar_region].size; base_addr = d->dev.io_regions[bar_region].addr; PRINT_DEBUG("\n%s: mem_write BAR Base address=0x%x Addr=0x%x val=0x%x bar=%d\n", __FUNCTION__, base_addr, addr, val, bar_region); PRINT_DEBUG("\n%s: mem_write REAL address: 0x%x\n", __FUNCTION__, base_addr + addr); PRINT_DEBUG("\n%s: mem_write Addr=0x%x val=0x%x bar=%d\n", __FUNCTION__, addr-reg_base, val, bar_region); do_socket_transaction(opaque, write, addr, val, len); } static uint64_t pcie_msix_mem_read(void *opaque, uint32_t addr, int len) { uint64_t val = -1; uint32_t reg_base, reg_size; uint32_t base_addr; int bar_region = 0; // Aries only has BAR 0/1 since its 64bit PCIE_MSIX_DEVState *d = opaque; bool read = true; reg_base = d->dev.io_regions[bar_region].addr; reg_size = d->dev.io_regions[bar_region].size; base_addr = d->dev.io_regions[bar_region].addr; PRINT_DEBUG("\n%s: mem_read BAR Base address=0x%x Addr=0x%x val=0x%x bar=%d\n", __FUNCTION__, base_addr, addr, val, bar_region); PRINT_DEBUG("\n%s: mem_read REAL address: 0x%x\n", __FUNCTION__, base_addr + addr); val = do_socket_transaction(opaque, read, addr, val, len); PRINT_DEBUG("\n%s: mem_read Addr=0x%x val=0x%x bar=%d\n", __FUNCTION__, addr-reg_base, val, bar_region); return val; } static void pcie_msix_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) { pcie_msix_mem_write(opaque, addr, val, 1); } static void pcie_msix_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) { pcie_msix_mem_write(opaque, addr, val, 2); } static void pcie_msix_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) { pcie_msix_mem_write(opaque, addr, val, 4); } static bool trans64wr_msl; // Most Significant LWord static uint64_t trans64wr_val; static bool trans64rd_msl; // Most Significant LWord static uint64_t trans64rd_val; static void pcie_msix_mem_writed(void *opaque, target_phys_addr_t addr, uint32_t val) { if (!trans64wr_msl) { trans64wr_val = val; trans64wr_msl = true; } else { uint64_t temp = val; trans64wr_val |= (temp << 32); pcie_msix_mem_write(opaque, addr, trans64wr_val, 8); trans64wr_msl = false; } } static uint32_t pcie_msix_mem_readb(void *opaque, target_phys_addr_t addr) { return pcie_msix_mem_read(opaque, addr, 1); } static uint32_t pcie_msix_mem_readw(void *opaque, target_phys_addr_t addr) { return pcie_msix_mem_read(opaque, addr, 2); } static uint32_t pcie_msix_mem_readl(void *opaque, target_phys_addr_t addr) { return pcie_msix_mem_read(opaque, addr, 4); } static uint32_t pcie_msix_mem_readd(void *opaque, target_phys_addr_t addr) { if (!trans64rd_msl) { trans64rd_val = pcie_msix_mem_read(opaque, addr, 8); trans64rd_msl = true; return (trans64rd_val & 0xffffffff); } else { trans64rd_msl = false; return (trans64rd_val >> 32); } } static CPUWriteMemoryFunc * const pcie_msix_mem_write_fn[] = { pcie_msix_mem_writeb, pcie_msix_mem_writew, pcie_msix_mem_writel, pcie_msix_mem_writed }; static CPUReadMemoryFunc * const pcie_msix_mem_read_fn[] = { pcie_msix_mem_readb, pcie_msix_mem_readw, pcie_msix_mem_readl, pcie_msix_mem_readd }; static void pcie_msix_mem_map(PCIDevice *dev, int region_num, pcibus_t addr, pcibus_t size, int type) { uint32_t msix_mem_bar = 0; PCIE_MSIX_DEVState *d = DO_UPCAST(PCIE_MSIX_DEVState, dev, dev); cpu_register_physical_memory(addr, BAR_Regions[msix_mem_bar][0], d->mmio_index); } static uint32_t pcie_msix_read_config(PCIDevice *d, uint32_t address, int len) { return pci_default_read_config(d, address, len); } static void pcie_msix_write_config(PCIDevice *d, uint32_t address, uint32_t val, int len) { pci_default_write_config(d, address, val, len); msix_write_config(d, address, val, len); /*pcie_cap_deverr_write_config(d, address, val, len); pcie_cap_flr_write_config(d, address, val, len); pcie_aer_write_config_vbridge(d, address, val, len); pcie_aer_write_config(d, address, val, len);*/ } static void pcie_msix_reset(DeviceState *qdev) { PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev); msix_reset(d); pcie_cap_deverr_reset(d); } static void pcie_msix_flr(PCIDevice *d) { pci_device_reset(d); } static int pcie_msix_initfn(PCIDevice *pci_dev) { PCIE_MSIX_DEVState *d = DO_UPCAST(PCIE_MSIX_DEVState, dev, pci_dev); PCIBridge *br = DO_UPCAST(PCIBridge, dev, pci_dev); PCIEPort *p = DO_UPCAST(PCIEPort, br, br); int rc; PRINT_DEBUG("%s: PCIE MSIX Device init...\n", __FUNCTION__); int msix_mem_bar = 0; // Since its a 64bit BAR, we take up BAR0 & BAR1 d->vectors = 64; d->mmio_index = cpu_register_io_memory(pcie_msix_mem_read_fn, pcie_msix_mem_write_fn, d); rc = msix_init(&d->dev, d->vectors, msix_mem_bar, 0); /*if (!rc) { PRINT_DEBUG("%s: Registering Bar %i as I/O BAR\n", __FUNCTION__, msix_mem_bar); pci_register_bar(&d->dev, msix_mem_bar, msix_bar_size(&d->dev), PCI_BASE_ADDRESS_SPACE_MEMORY, msix_mmio_map); PRINT_DEBUG("%s: MSI-X initialized (%d vectors)\n", __FUNCTION__, d->vectors); } else { PRINT_DEBUG("%s: MSI-X initialization failed!\n", __FUNCTION__); return rc; }*/ // Activate the vectors /*for (i = 0; i < d->vectors; i++) { msix_vector_use(&d->dev, i); }*/ pci_register_bar(&d->dev, msix_mem_bar, BAR_Regions[msix_mem_bar][0], BAR_Regions[msix_mem_bar][1], pcie_msix_mem_map); rc = pci_pcie_cap_init(&d->dev, PCIE_MSIX_EXP_OFFSET, PCI_EXP_TYPE_ENDPOINT, p->port); if (rc < 0) { return rc; } pci_set_word(&d->dev.config[PCI_VENDOR_ID], g_cfg_init[0] & 0xffff); pci_set_word(&d->dev.config[PCI_DEVICE_ID], (g_cfg_init[0] >> 16) & 0xffff); pci_set_word(&d->dev.config[PCI_COMMAND], g_cfg_init[1] & 0xffff); pci_set_word(&d->dev.config[PCI_STATUS], (g_cfg_init[1] >> 16) & 0xffff); pci_set_byte(&d->dev.config[PCI_REVISION_ID], g_cfg_init[2] & 0xff); pci_set_byte(&d->dev.config[PCI_REVISION_ID+1], (g_cfg_init[2] >> 8) & 0xff); pci_set_word(&d->dev.config[PCI_REVISION_ID+2], (g_cfg_init[2] >> 16) & 0xffff); pci_set_byte(&d->dev.config[PCI_CACHE_LINE_SIZE], g_cfg_init[3] & 0xff); pci_set_byte(&d->dev.config[PCI_LATENCY_TIMER], (g_cfg_init[3] >> 8) & 0xff); pci_set_byte(&d->dev.config[PCI_HEADER_TYPE], (g_cfg_init[3] >> 16) & 0xff); //pci_set_byte(&d->dev.config[PCI_BIST], (g_cfg_init[3] >> 24) & 0xff); //pci_set_long(&d->dev.config[PCI_BASE_ADDRESS_0], g_cfg_init[4]); //pci_set_long(&d->dev.config[PCI_BASE_ADDRESS_1], g_cfg_init[5]); //pci_set_long(&d->dev.config[PCI_BASE_ADDRESS_2], g_cfg_init[6]); //pci_set_long(&d->dev.config[PCI_BASE_ADDRESS_3], g_cfg_init[7]); //pci_set_long(&d->dev.config[PCI_BASE_ADDRESS_4], g_cfg_init[8]); //pci_set_long(&d->dev.config[PCI_BASE_ADDRESS_5], g_cfg_init[9]); //pci_set_long(&d->dev.config[PCI_CARDBUS_CIS], g_cfg_init[0xa]); pci_set_word(&d->dev.config[PCI_SUBSYSTEM_VENDOR_ID],g_cfg_init[0xb] & 0xffff); pci_set_word(&d->dev.config[PCI_SUBSYSTEM_ID], (g_cfg_init[0xb] >> 16) & 0xffff); //pci_set_long(&d->dev.config[PCI_ROM_ADDRESS], g_cfg_init[0xc]); //pci_set_byte(&d->dev.config[PCI_CAPABILITY_LIST], g_cfg_init[0xd] & 0xff); pci_set_byte(&d->dev.config[PCI_INTERRUPT_LINE], g_cfg_init[0xf] & 0xff); pci_set_byte(&d->dev.config[PCI_INTERRUPT_PIN], (g_cfg_init[0xf] >> 8) & 0xff); pci_set_byte(&d->dev.config[PCI_MIN_GNT], (g_cfg_init[0xf] >> 16) & 0xff); pci_set_byte(&d->dev.config[PCI_MAX_LAT], (g_cfg_init[0xf] >> 24) & 0xff); // Power Capabilities pci_set_long(&d->dev.config[0x80], g_cfg_init[0x20]); pci_set_long(&d->dev.config[0x84], g_cfg_init[0x21]); pci_set_long(&d->dev.config[0x88], g_cfg_init[0x22]); pci_set_long(&d->dev.config[0x8c], g_cfg_init[0x23]); // MSI-X Capabilities pci_set_long(&d->dev.config[0xb0], g_cfg_init[0x2c]); pci_set_long(&d->dev.config[0xb4], g_cfg_init[0x2d]); pci_set_long(&d->dev.config[0xb8], g_cfg_init[0x2e]); pci_set_long(&d->dev.config[0xbc], g_cfg_init[0x2f]); pci_set_long(&d->dev.config[0xc0], g_cfg_init[0x30]); pci_set_long(&d->dev.config[0xc4], g_cfg_init[0x31]); pci_set_long(&d->dev.config[0xc8], g_cfg_init[0x32]); pci_set_long(&d->dev.config[0xcc], g_cfg_init[0x33]); pci_set_long(&d->dev.config[0xd0], g_cfg_init[0x34]); pci_set_long(&d->dev.config[0xd4], g_cfg_init[0x35]); pci_set_long(&d->dev.config[0xd8], g_cfg_init[0x36]); pci_set_long(&d->dev.config[0xdc], g_cfg_init[0x37]); pci_set_long(&d->dev.config[0xe0], g_cfg_init[0x38]); pci_set_long(&d->dev.config[0xe4], g_cfg_init[0x39]); pci_set_long(&d->dev.config[0xe8], g_cfg_init[0x3a]); pci_set_long(&d->dev.config[0xec], g_cfg_init[0x3b]); pci_set_long(&d->dev.config[0xf0], g_cfg_init[0x3c]); PRINT_DEBUG("%s: Init done\n", __FUNCTION__); return 0; } static int pcie_msix_exitfn(PCIDevice *pci_dev) { pcie_aer_exit(pci_dev); msix_uninit(pci_dev); return pci_pcie_cap_exit(pci_dev); } PCIDevice* pcie_msix_init(PCIBus *bus) { return pci_create_simple(bus, -1, "pcie_msix_device"); } static const VMStateDescription vmstate_pcie_msix = { .name = "pcie-msix-device", .version_id = 1, .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_PCIE_DEVICE(dev, PCIE_MSIX_DEVState), VMSTATE_STRUCT(dev.aer_log, PCIE_MSIX_DEVState, 0, vmstate_pcie_aer_log, struct pcie_aer_log), VMSTATE_END_OF_LIST() } }; static PCIDeviceInfo pcie_msix_info = { .qdev.name = PCIE_MSIX_DEVICE, .qdev.desc = "PCIE MSIX device template", .qdev.size = sizeof(PCIE_MSIX_DEVState), .qdev.reset = pcie_msix_reset, .qdev.vmsd = &vmstate_pcie_msix, .is_express = 1, .config_read = pcie_msix_read_config, .config_write = pcie_msix_write_config, .init = pcie_msix_initfn, .exit = pcie_msix_exitfn, .qdev.props = (Property[]) { DEFINE_PROP_UINT32("vectors", PCIE_MSIX_DEVState, vectors, PCIE_MSIX_MSI_NR_VECTOR), DEFINE_PROP_UINT16("aer_log_max", PCIESlot, port.br.dev.aer_log.log_max, PCIE_AER_LOG_MAX_DEFAULT), DEFINE_PROP_END_OF_LIST(), } }; static void pcie_msix_register(void) { pci_qdev_register(&pcie_msix_info); } device_init(pcie_msix_register);