vmd: Initial SPDK VMD baseline code
Signed-off-by: Orden Smith <orden.e.smith@intel.com> Signed-off-by: Darek Stojaczyk <dariusz.stojaczyk@intel.com> Signed-off-by: Wojciech Malikowski <wojciech.malikowski@intel.com> Change-Id: I4a3c4d1ca8d0b18201edf99a67a3d75ca7ab8153 Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/449499 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com>
This commit is contained in:
parent
4de4b78b28
commit
68eda44030
75
include/spdk/vmd.h
Normal file
75
include/spdk/vmd.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*-
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright (c) Intel Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* VMD driver public interface
|
||||
*/
|
||||
|
||||
#ifndef SPDK_VMD_H
|
||||
#define SPDK_VMD_H
|
||||
|
||||
#include "spdk/stdinc.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "spdk/config.h"
|
||||
#include "spdk/env.h"
|
||||
|
||||
/*
|
||||
* Takes an input VMD D-BDF, probes it and attaches to it. The resulting vmd
|
||||
* adapter is placed in a vmd container. If input BDF is NULL, then all VMD
|
||||
* probed is consumed in the application VMD container list.
|
||||
*
|
||||
* \param vmd_bdf VMD BDF
|
||||
*
|
||||
* \return number of VMD devices available in the system
|
||||
*/
|
||||
int spdk_vmd_probe(struct spdk_pci_addr *vmd_bdf);
|
||||
|
||||
/*
|
||||
* Returns a list of nvme devices found on the given vmd pci BDF.
|
||||
*
|
||||
* \param vmd_addr pci BDF of the vmd device to return end device list
|
||||
* \param nvme_list buffer of up to MAX_VMD_TARGET to return spdk_pci_device array.
|
||||
*
|
||||
* \return Returns count of nvme device attached to input VMD.
|
||||
*/
|
||||
int spdk_vmd_pci_device_list(struct spdk_pci_addr vmd_addr, struct spdk_pci_device *nvme_list);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SPDK_VMD_H */
|
@ -35,7 +35,7 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/..)
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
|
||||
DIRS-y += bdev blob blobfs conf copy event json jsonrpc \
|
||||
log lvol net rpc sock thread trace util nvme nvmf scsi ioat \
|
||||
log lvol net rpc sock thread trace util nvme vmd nvmf scsi ioat \
|
||||
ut_mock iscsi notify
|
||||
ifeq ($(OS),Linux)
|
||||
DIRS-y += nbd ftl
|
||||
|
40
lib/vmd/Makefile
Normal file
40
lib/vmd/Makefile
Normal file
@ -0,0 +1,40 @@
|
||||
#
|
||||
# BSD LICENSE
|
||||
#
|
||||
# Copyright (c) Intel Corporation.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in
|
||||
# the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Intel Corporation nor the names of its
|
||||
# contributors may be used to endorse or promote products derived
|
||||
# from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
|
||||
C_SRCS = vmd.c vmdpci.c
|
||||
LIBNAME = vmd
|
||||
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk
|
198
lib/vmd/vmd.c
Normal file
198
lib/vmd/vmd.c
Normal file
@ -0,0 +1,198 @@
|
||||
/*-
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright (c) Intel Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "vmdpci.h"
|
||||
|
||||
#include "spdk/stdinc.h"
|
||||
|
||||
/*
|
||||
* Container for all VMD adapter probed in the system.
|
||||
*/
|
||||
struct vmd_container {
|
||||
uint32_t count;
|
||||
/* can target specific vmd or all vmd when null */
|
||||
struct spdk_pci_addr *vmd_target_addr;
|
||||
vmd_adapter vmd[MAX_VMD_SUPPORTED];
|
||||
} vmd_container;
|
||||
|
||||
static struct vmd_container g_vmd_container;
|
||||
|
||||
static int
|
||||
vmd_map_bars(vmd_adapter *vmd, struct spdk_pci_device *dev)
|
||||
{
|
||||
if (!(vmd && dev)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int rc = spdk_pci_device_map_bar(dev, 0, (void **)&vmd->cfg_vaddr,
|
||||
&vmd->cfgbar, &vmd->cfgbar_size);
|
||||
if (rc == 0) {
|
||||
rc = spdk_pci_device_map_bar(dev, 2, (void **)&vmd->mem_vaddr,
|
||||
&vmd->membar, &vmd->membar_size);
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
rc = spdk_pci_device_map_bar(dev, 4, (void **)&vmd->msix_vaddr,
|
||||
&vmd->msixbar, &vmd->msixbar_size);
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
vmd->physical_addr = vmd->membar;
|
||||
vmd->current_addr_size = vmd->membar_size;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
vmd_enumerate_devices(vmd_adapter *vmd)
|
||||
{
|
||||
if (vmd == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
vmd->vmd_bus.vmd = vmd;
|
||||
vmd->vmd_bus.secondary_bus = vmd->vmd_bus.subordinate_bus = 0;
|
||||
vmd->vmd_bus.primary_bus = vmd->vmd_bus.bus_number = 0;
|
||||
vmd->vmd_bus.domain = vmd->pci.addr.domain;
|
||||
|
||||
return vmd_scan_pcibus(&vmd->vmd_bus);
|
||||
}
|
||||
|
||||
static int
|
||||
vmd_enum_cb(void *ctx, struct spdk_pci_device *pci_dev)
|
||||
{
|
||||
uint32_t cmd_reg = 0;
|
||||
char bdf[32] = {0};
|
||||
struct vmd_container *vmd_c = ctx;
|
||||
size_t i;
|
||||
|
||||
if (!(pci_dev && ctx)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If vmd target addr is NULL, then all spdk returned devices are consumed
|
||||
*/
|
||||
if (vmd_c->vmd_target_addr &&
|
||||
spdk_pci_addr_compare(&pci_dev->addr, vmd_c->vmd_target_addr)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
spdk_pci_device_cfg_read32(pci_dev, &cmd_reg, 4);
|
||||
cmd_reg |= 0x6; /* PCI bus master/memory enable. */
|
||||
spdk_pci_device_cfg_write32(pci_dev, cmd_reg, 4);
|
||||
|
||||
spdk_pci_addr_fmt(bdf, sizeof(bdf), &pci_dev->addr);
|
||||
SPDK_DEBUGLOG(SPDK_LOG_VMD, "Found a VMD[ %d ] at %s\n", vmd_c->count, bdf);
|
||||
|
||||
/* map vmd bars */
|
||||
i = vmd_c->count;
|
||||
vmd_c->vmd[i].pci = *pci_dev;
|
||||
vmd_c->vmd[i].vmd_index = i;
|
||||
vmd_c->vmd[i].domain =
|
||||
(pci_dev->addr.bus << 16) | (pci_dev->addr.dev << 8) | pci_dev->addr.func;
|
||||
vmd_c->vmd[i].max_pci_bus = PCI_MAX_BUS_NUMBER;
|
||||
if (vmd_map_bars(&vmd_c->vmd[i], pci_dev) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SPDK_DEBUGLOG(SPDK_LOG_VMD, "vmd config bar(%p) vaddr(%p) size(%x)\n",
|
||||
(void *)vmd_c->vmd[i].cfgbar, (void *)vmd_c->vmd[i].cfg_vaddr,
|
||||
(uint32_t)vmd_c->vmd[i].cfgbar_size);
|
||||
SPDK_DEBUGLOG(SPDK_LOG_VMD, "vmd mem bar(%p) vaddr(%p) size(%x)\n",
|
||||
(void *)vmd_c->vmd[i].membar, (void *)vmd_c->vmd[i].mem_vaddr,
|
||||
(uint32_t)vmd_c->vmd[i].membar_size);
|
||||
SPDK_DEBUGLOG(SPDK_LOG_VMD, "vmd msix bar(%p) vaddr(%p) size(%x)\n\n",
|
||||
(void *)vmd_c->vmd[i].msixbar, (void *)vmd_c->vmd[i].msix_vaddr,
|
||||
(uint32_t)vmd_c->vmd[i].msixbar_size);
|
||||
|
||||
vmd_c->count = i + 1;
|
||||
|
||||
vmd_enumerate_devices(&vmd_c->vmd[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
vmd_dev_init(vmd_pci_device *dev)
|
||||
{
|
||||
uint8_t bdf[32];
|
||||
|
||||
/* TODO: Initialize device */
|
||||
if (vmd_is_supported_device(dev)) {
|
||||
spdk_pci_addr_fmt(bdf, sizeof(bdf), &dev->pci.addr);
|
||||
SPDK_DEBUGLOG(SPDK_LOG_VMD, "Initalizing NVMe device at %s\n", bdf);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
spdk_vmd_pci_device_list(struct spdk_pci_addr vmd_addr, struct spdk_pci_device *nvme_list)
|
||||
{
|
||||
int cnt = 0;
|
||||
|
||||
if (!nvme_list) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_VMD_TARGET; ++i) {
|
||||
if (spdk_pci_addr_compare(&vmd_addr, &g_vmd_container.vmd[i].pci.addr) == 0) {
|
||||
vmd_pci_bus *bus = g_vmd_container.vmd[i].bus_list;
|
||||
while (bus != NULL) {
|
||||
vmd_pci_device *dev = bus->dev_list;
|
||||
while (dev != NULL) {
|
||||
nvme_list[cnt++] = dev->pci;
|
||||
if (!dev->is_hooked) {
|
||||
vmd_dev_init(dev);
|
||||
dev->is_hooked = 1;
|
||||
}
|
||||
dev = dev->next;
|
||||
}
|
||||
bus = bus->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int
|
||||
spdk_vmd_probe(struct spdk_pci_addr *vmd_bdf)
|
||||
{
|
||||
g_vmd_container.vmd_target_addr = vmd_bdf;
|
||||
spdk_pci_enumerate(spdk_pci_vmd_get_driver(), vmd_enum_cb, &g_vmd_container);
|
||||
g_vmd_container.vmd_target_addr = NULL;
|
||||
|
||||
return g_vmd_container.count;
|
||||
}
|
||||
|
||||
SPDK_LOG_REGISTER_COMPONENT("vmd", SPDK_LOG_VMD)
|
535
lib/vmd/vmd_spec.h
Normal file
535
lib/vmd/vmd_spec.h
Normal file
@ -0,0 +1,535 @@
|
||||
/*-
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright (c) Intel Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef VMD_SPEC_H
|
||||
#define VMD_SPEC_H
|
||||
|
||||
#define SUPPPORT_ALL_SSDS
|
||||
|
||||
#define MAX_VMD_SUPPORTED 48 /* max number of vmd controllers in a system - */
|
||||
/* up to 6 per cpu */
|
||||
#define MAX_VMD_TARGET 24
|
||||
#define VMD_DOMAIN_START 0x201D
|
||||
|
||||
#define PCI_INVALID_VENDORID 0xFFFF
|
||||
#define ONE_MB (1<<20)
|
||||
#define PCI_OFFSET_OF(object, member) ((uint32_t)&((object*)0)->member)
|
||||
#define TWOS_COMPLEMENT(value) (~(value) + 1)
|
||||
|
||||
/*
|
||||
* BAR assignment constants
|
||||
*/
|
||||
#define PCI_DWORD_SHIFT 32
|
||||
#define PCI_BASE_ADDR_MASK 0xFFFFFFF0
|
||||
#define PCI_BAR_MEMORY_MASK 0x0000000F
|
||||
#define PCI_BAR_MEMORY_MEM_IND 0x1
|
||||
#define PCI_BAR_MEMORY_TYPE 0x6
|
||||
#define PCI_BAR_MEMORY_PREFETCH 0x8
|
||||
#define PCI_BAR_MEMORY_TYPE_32 0x0
|
||||
#define PCI_BAR_MEMORY_TYPE_64 0x4
|
||||
#define PCI_BAR_MB_MASK 0xFFFFF
|
||||
#define PCI_PCI_BRIDGE_ADDR_DEF 0xFFF0
|
||||
#define PCI_BRIDGE_MEMORY_MASK 0xFFF0
|
||||
#define PCI_BRIDGE_PREFETCH_64 0x0001
|
||||
#define PCI_BRIDGE_MEMORY_SHIFT 16
|
||||
#define PCI_CONFIG_ACCESS_DELAY 500
|
||||
|
||||
#define PCI_MAX_CFG_SIZE 0x1000
|
||||
|
||||
#define PCI_HEADER_TYPE 0x0e
|
||||
#define PCI_HEADER_TYPE_NORMAL 0
|
||||
#define PCI_HEADER_TYPE_BRIDGE 1
|
||||
#define PCI_MULTI_FUNCTION 0x80
|
||||
|
||||
#define PCI_COMMAND_MEMORY 0x2
|
||||
#define PCI_COMMAND_MASTER 0x4
|
||||
|
||||
#define PCIE_TYPE_FLAGS 0xf0
|
||||
#define PCIE_TYPE_SHIFT 4
|
||||
#define PCIE_TYPE_ROOT_PORT 0x4
|
||||
#define PCIE_TYPE_DOWNSTREAM 0x6
|
||||
|
||||
#define PCI_CLASS_STORAGE_EXPRESS 0x010802
|
||||
#define ADDR_ELEM_COUNT 32
|
||||
#define PCI_MAX_BUS_NUMBER 0x7F
|
||||
#define RESERVED_HOTPLUG_BUSES 1
|
||||
#define isHotPlugCapable(slotCap) ((slotCap) & (1<<6))
|
||||
#define CONFIG_OFFSET_ADDR(bus, device, function, reg) (((bus)<<20) | (device)<<15 | (function<<12) | (reg))
|
||||
#define BRIDGE_BASEREG(reg) (0xFFF0 & ((reg)>>16))
|
||||
|
||||
#define MISCCTRLSTS_0_OFFSET 0x188
|
||||
#define ENABLE_ACPI_MODE_FOR_HOTPLUG (1 << 3)
|
||||
|
||||
/* Bit encodings for Command Register */
|
||||
#define IO_SPACE_ENABLE 0x0001
|
||||
#define MEMORY_SPACE_ENABLE 0x0002
|
||||
#define BUS_MASTER_ENABLE 0x0004
|
||||
|
||||
/* Bit encodings for Status Register */
|
||||
#define PCI_CAPABILITIES_LIST 0x0010
|
||||
#define PCI_RECEIVED_TARGET_ABORT 0x1000
|
||||
#define PCI_RECEIVED_MASTER_ABORT 0x2000
|
||||
#define PCI_SIGNALED_SYSTEM_ERROR 0x4000
|
||||
#define PCI_DETECTED_PARITY_ERROR 0x8000
|
||||
|
||||
/* Capability IDs */
|
||||
#define CAPABILITY_ID_POWER_MANAGEMENT 0x01
|
||||
#define CAPABILITY_ID_MSI 0x05
|
||||
#define CAPABILITY_ID_PCI_EXPRESS 0x10
|
||||
#define CAPABILITY_ID_MSIX 0x11
|
||||
|
||||
#define PCI_MSIX_ENABLE (1 << 15) /* bit 15 of MSIX Message Control */
|
||||
#define PCI_MSIX_FUNCTION_MASK (1 << 14) /* bit 14 of MSIX Message Control */
|
||||
|
||||
/* extended capability */
|
||||
#define EXTENDED_CAPABILITY_OFFSET 0x100
|
||||
#define DEVICE_SERIAL_NUMBER_CAP_ID 0x3
|
||||
|
||||
#define BAR_SIZE (1 << 20)
|
||||
|
||||
typedef struct _enhanced_capability_hdr {
|
||||
uint16_t capability_id;
|
||||
uint16_t version: 4;
|
||||
uint16_t next: 12;
|
||||
} pci_enhanced_capability_header;
|
||||
|
||||
typedef struct _SerialNumberCapability {
|
||||
pci_enhanced_capability_header hdr;
|
||||
uint32_t sn_low;
|
||||
uint32_t sn_hi;
|
||||
} serial_number_capability;
|
||||
|
||||
typedef struct _PciHeaderCommon {
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint16_t command;
|
||||
uint16_t status;
|
||||
uint32_t rev_class;
|
||||
uint8_t cache_line_size;
|
||||
uint8_t master_lat_timer;
|
||||
uint8_t header_type;
|
||||
uint8_t BIST;
|
||||
uint8_t rsvd12[36];
|
||||
uint8_t cap_pointer;
|
||||
uint8_t rsvd53[7];
|
||||
uint8_t int_line;
|
||||
uint8_t int_pin;
|
||||
uint8_t rsvd62[2];
|
||||
} pci_header_common;
|
||||
|
||||
typedef struct _PciTypeZeroHeader {
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint16_t command;
|
||||
uint16_t status;
|
||||
uint32_t rev_class;
|
||||
uint8_t cache_line_size;
|
||||
uint8_t master_lat_timer;
|
||||
uint8_t header_type;
|
||||
uint8_t BIST;
|
||||
uint32_t BAR[6];
|
||||
uint32_t carbus_cis_pointer;
|
||||
uint16_t ssvid;
|
||||
uint16_t ssid;
|
||||
uint32_t exp_rom_base_addr;
|
||||
uint8_t cap_pointer;
|
||||
uint8_t rsvd53[7];
|
||||
uint8_t intLine;
|
||||
uint8_t int_pin;
|
||||
uint8_t min_gnt;
|
||||
uint8_t max_lat;
|
||||
} pci_header_zero;
|
||||
|
||||
typedef struct _PciTypeOneHeader {
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint16_t command;
|
||||
uint16_t status;
|
||||
uint32_t rev_class;
|
||||
uint8_t cache_line_size;
|
||||
uint8_t master_lat_timer;
|
||||
uint8_t header_type;
|
||||
uint8_t BIST;
|
||||
uint32_t BAR[2];
|
||||
uint8_t primary;
|
||||
uint8_t secondary;
|
||||
uint8_t subordinate;
|
||||
uint8_t secondary_lat_timer;
|
||||
uint8_t io_base;
|
||||
uint8_t io_limit;
|
||||
uint16_t secondary_status;
|
||||
uint16_t mem_base;
|
||||
uint16_t mem_limit;
|
||||
uint16_t prefetch_base;
|
||||
uint16_t prefetch_limit;
|
||||
uint32_t prefetch_base_upper;
|
||||
uint32_t prefetch_limit_upper;
|
||||
uint16_t io_base_upper;
|
||||
uint16_t io_limit_upper;
|
||||
uint8_t cap_pointer;
|
||||
uint8_t rsvd53[3];
|
||||
uint32_t exp_romBase_addr;
|
||||
uint8_t int_line;
|
||||
uint8_t int_pin;
|
||||
uint16_t bridge_control;
|
||||
} pci_header_one;
|
||||
|
||||
|
||||
typedef struct _PciCapHdr {
|
||||
uint8_t capability_id;
|
||||
uint8_t next;
|
||||
} pci_capabilities_header;
|
||||
|
||||
/*
|
||||
* MSI capability structure for msi interrupt vectors
|
||||
*/
|
||||
#define MAX_MSIX_TABLE_SIZE 0x800
|
||||
#define MSIX_ENTRY_VECTOR_CTRL_MASKBIT 1
|
||||
#define PORT_INT_VECTOR 0;
|
||||
#define CLEAR_MSIX_DESTINATION_ID 0xfff00fff
|
||||
struct PCI_MSI_CAPABILITY {
|
||||
pci_capabilities_header header;
|
||||
union _MsiControl {
|
||||
uint16_t as_uint16_t;
|
||||
struct _PCI_MSI_MESSAGE_CONTROL {
|
||||
uint16_t msi_enable : 1;
|
||||
uint16_t multiple_message_capable : 3;
|
||||
uint16_t multiple_message_enable : 3;
|
||||
uint16_t capable_of_64bits : 1;
|
||||
uint16_t per_vector_mask_capable : 1;
|
||||
uint16_t reserved : 7;
|
||||
} bit;
|
||||
} message_control;
|
||||
union {
|
||||
struct _PCI_MSI_MESSAGE_ADDRESS {
|
||||
uint32_t reserved : 2;
|
||||
uint32_t address : 30;
|
||||
} reg;
|
||||
uint32_t raw;
|
||||
} message_address_lower;
|
||||
union {
|
||||
struct _Option32_bit {
|
||||
uint16_t message_data;
|
||||
} option32_bit;
|
||||
struct _Option64_bit {
|
||||
uint32_t message_address_upper;
|
||||
uint16_t message_data;
|
||||
uint16_t reserved;
|
||||
uint32_t mask_bits;
|
||||
uint32_t pending_bits;
|
||||
} option64_bit;
|
||||
};
|
||||
};
|
||||
typedef struct PCI_MSI_CAPABILITY pci_msi_cap;
|
||||
|
||||
typedef struct PcixTablePointer {
|
||||
union {
|
||||
struct {
|
||||
uint32_t BaseIndexRegister : 3;
|
||||
uint32_t Reserved : 29;
|
||||
} TableBIR;
|
||||
uint32_t TableOffset;
|
||||
};
|
||||
} pcix_table_pointer;
|
||||
|
||||
typedef struct _PciMsixCapability {
|
||||
struct _PciCapHdr header;
|
||||
union _MsixControl {
|
||||
uint16_t as_uint16_t;
|
||||
struct msg_ctrl {
|
||||
uint16_t table_size : 11;
|
||||
uint16_t reserved : 3;
|
||||
uint16_t function_mask : 1;
|
||||
uint16_t msix_enable : 1;
|
||||
} bit;
|
||||
} message_control;
|
||||
|
||||
pcix_table_pointer message_table;
|
||||
pcix_table_pointer pba_table;
|
||||
} pci_msix_capability;
|
||||
|
||||
typedef struct _pci_misx_table_entry {
|
||||
volatile uint32_t message_addr_lo;
|
||||
volatile uint32_t message_addr_hi;
|
||||
volatile uint32_t message_data;
|
||||
volatile uint32_t vector_control;
|
||||
} pci_msix_table_entry;
|
||||
|
||||
/*
|
||||
* Pci express capability
|
||||
*/
|
||||
enum PciExpressCapabilities {
|
||||
LegacyEndpoint = 0x1, /* 0001b Legacy PCI Express Endpoint */
|
||||
ExpressEndpoint = 0x0, /* 0000b PCI Express Endpoint */
|
||||
RootComplexRootPort = 0x4, /* 0100b Root Port of PCI Express Root Complex* */
|
||||
SwitchUpstreamPort = 0x5, /* 0101b Upstream Port of PCI Express Switch* */
|
||||
SwitchDownStreamPort = 0x6, /* 0110b Downstream Port of PCI Express Switch* */
|
||||
ExpressToPciBridge = 0x7, /* 0111b PCI Express to PCI/PCI-X Bridge* */
|
||||
PciToExpressBridge = 0x8, /* 1000b PCI/PCI-X to PCI Express Bridge* */
|
||||
RCIntegratedEndpoint = 0x9, /* 1001b Root Complex Integrated Endpoint */
|
||||
RootComplexEventCollector = 0xa, /* 1010b Root Complex Event Collector */
|
||||
InvalidCapability = 0xff
|
||||
};
|
||||
|
||||
typedef union _EXPRESS_CAPABILITIES_REGISTER {
|
||||
struct {
|
||||
uint16_t capability_version : 4;
|
||||
uint16_t device_type : 4;
|
||||
uint16_t slot_implemented : 1;
|
||||
uint16_t interrupt_message_number : 5;
|
||||
uint16_t rsv : 2;
|
||||
} bit_field;
|
||||
uint16_t as_uint16_t;
|
||||
} express_capability_register;
|
||||
|
||||
typedef union _EXPRESS_DEVICE_CAPABILITIES_REGISTER {
|
||||
struct {
|
||||
uint32_t max_payload_size_supported : 3;
|
||||
uint32_t phantom_functions_supported : 2;
|
||||
uint32_t extended_tag_supported : 1;
|
||||
uint32_t L0s_acceptable_latency : 3;
|
||||
uint32_t L1_acceptable_latency : 3;
|
||||
uint32_t undefined : 3;
|
||||
uint32_t role_based_error_reporting : 1;
|
||||
uint32_t rsvd1 : 2;
|
||||
uint32_t captured_slot_power_limit : 8;
|
||||
uint32_t captured_slot_power_limit_scale : 2;
|
||||
uint32_t rsvd2 : 4;
|
||||
} bit_field;
|
||||
uint32_t as_uint32_t;
|
||||
} express_device_capability_register;
|
||||
|
||||
/*
|
||||
* The low 3 bits of the PCI Express device control register dictate whether
|
||||
* a device that implements AER routes error messages to the root complex.
|
||||
* This mask is used when programming the AER bits in the device control
|
||||
* register.
|
||||
*/
|
||||
#define EXPRESS_AER_DEVICE_CONTROL_MASK 0x07;
|
||||
typedef union _EXPRESS_DEVICE_CONTROL_REGISTER {
|
||||
struct {
|
||||
uint16_t correctable_error_enable : 1;
|
||||
uint16_t non_fatal_error_enable : 1;
|
||||
uint16_t fatal_error_enable : 1;
|
||||
uint16_t unsupported_request_error_enable : 1;
|
||||
uint16_t enable_relaxed_order : 1;
|
||||
uint16_t max_payload_size : 3;
|
||||
uint16_t extended_tag_enable : 1;
|
||||
uint16_t phantom_functions_enable : 1;
|
||||
uint16_t aux_power_enable : 1;
|
||||
uint16_t no_snoop_enable : 1;
|
||||
uint16_t max_read_request_size : 3;
|
||||
uint16_t bridge_config_retry_enable : 1;
|
||||
} bit_field;
|
||||
uint16_t as_uint16_t;;
|
||||
} express_device_control_register;
|
||||
|
||||
/*
|
||||
* The low 4 bits of the PCI Express device status register hold AER device
|
||||
* status. This mask is used when programming the AER bits in the device status
|
||||
* register.
|
||||
*/
|
||||
#define EXPRESS_AER_DEVICE_STATUS_MASK 0x0F;
|
||||
typedef union _EXPRESS_DEVICE_STATUS_REGISTER {
|
||||
struct {
|
||||
uint16_t CorrectableErrorDetected : 1;
|
||||
uint16_t NonFatalErrorDetected : 1;
|
||||
uint16_t FatalErrorDetected : 1;
|
||||
uint16_t UnsupportedRequestDetected : 1;
|
||||
uint16_t AuxPowerDetected : 1;
|
||||
uint16_t TransactionsPending : 1;
|
||||
uint16_t Rsvd : 10;
|
||||
} bit_field;
|
||||
uint16_t Asuint16_t;
|
||||
} express_device_status_register;
|
||||
|
||||
typedef union _EXPRESS_LINK_CAPABILITIES_REGISTER {
|
||||
struct {
|
||||
uint32_t MaximumLinkSpeed : 4;
|
||||
uint32_t MaximumLinkWidth : 6;
|
||||
uint32_t ActiveStatePMSupport : 2;
|
||||
uint32_t L0sExitLatency : 3;
|
||||
uint32_t L1ExitLatency : 3;
|
||||
uint32_t ClockPowerManagement : 1;
|
||||
uint32_t SurpriseDownErrorReportingCapable : 1;
|
||||
uint32_t DataLinkLayerActiveReportingCapable : 1;
|
||||
uint32_t LinkBandwidthNotificationCapability : 1;
|
||||
uint32_t AspmOptionalityCompliance : 1;
|
||||
uint32_t Rsvd : 1;
|
||||
uint32_t PortNumber : 8;
|
||||
} bit_field;
|
||||
uint32_t Asuint32_t;
|
||||
} express_link_capability_register;
|
||||
|
||||
typedef union _EXPRESS_LINK_CONTROL_REGISTER {
|
||||
struct {
|
||||
uint16_t ActiveStatePMControl : 2;
|
||||
uint16_t Rsvd1 : 1;
|
||||
uint16_t ReadCompletionBoundary : 1;
|
||||
uint16_t LinkDisable : 1;
|
||||
uint16_t RetrainLink : 1;
|
||||
uint16_t CommonClockConfig : 1;
|
||||
uint16_t ExtendedSynch : 1;
|
||||
uint16_t EnableClockPowerManagement : 1;
|
||||
uint16_t Rsvd2 : 7;
|
||||
} bit_field;
|
||||
uint16_t Asuint16_t;
|
||||
} express_link_control_register;
|
||||
|
||||
typedef union _EXPRESS_LINK_STATUS_REGISTER {
|
||||
struct {
|
||||
uint16_t LinkSpeed : 4;
|
||||
uint16_t LinkWidth : 6;
|
||||
uint16_t Undefined : 1;
|
||||
uint16_t LinkTraining : 1;
|
||||
uint16_t SlotClockConfig : 1;
|
||||
uint16_t DataLinkLayerActive : 1;
|
||||
uint16_t Rsvd : 2;
|
||||
} bitField;
|
||||
uint16_t Asuint16_t;
|
||||
} express_link_status_register;
|
||||
|
||||
typedef union _EXPRESS_SLOT_CAPABILITIES_REGISTER {
|
||||
struct {
|
||||
uint32_t attention_button_present : 1;
|
||||
uint32_t power_controller_present : 1;
|
||||
uint32_t MRL_sensor_present : 1;
|
||||
uint32_t attention_indicator_present : 1;
|
||||
uint32_t power_indicator_present : 1;
|
||||
uint32_t hotplug_surprise : 1;
|
||||
uint32_t hotplug_capable : 1;
|
||||
uint32_t slot_power_limit : 8;
|
||||
uint32_t slotPower_limit_scale : 2;
|
||||
uint32_t electromechanical_lock_present : 1;
|
||||
uint32_t no_command_completed_support : 1;
|
||||
uint32_t physical_slot_number : 13;
|
||||
} bit_field;
|
||||
uint32_t as_uint32_t;
|
||||
} express_slot_capabiliies_register;
|
||||
|
||||
typedef union _EXPRESS_SLOT_CONTROL_REGISTER {
|
||||
struct {
|
||||
uint16_t attention_button_enable : 1;
|
||||
uint16_t power_fault_detect_enable : 1;
|
||||
uint16_t MRLsensor_enable : 1;
|
||||
uint16_t presence_detect_enable : 1;
|
||||
uint16_t command_completed_enable : 1;
|
||||
uint16_t hotplug_interrupt_enable : 1;
|
||||
uint16_t attention_indicator_control : 2;
|
||||
uint16_t power_indicator_control : 2;
|
||||
uint16_t power_controller_control : 1;
|
||||
uint16_t electromechanical_lockcontrol : 1;
|
||||
uint16_t datalink_state_change_enable : 1;
|
||||
uint16_t Rsvd : 3;
|
||||
} bit_field;
|
||||
uint16_t as_uint16_t;
|
||||
} express_slot_control_register;
|
||||
|
||||
typedef union _EXPRESS_SLOT_STATUS_REGISTER {
|
||||
struct {
|
||||
uint16_t attention_button_pressed : 1;
|
||||
uint16_t power_fault_detected : 1;
|
||||
uint16_t MRL_sensor_changed : 1;
|
||||
uint16_t presence_detect_changed : 1;
|
||||
uint16_t command_completed : 1;
|
||||
uint16_t MRL_sensor_state : 1;
|
||||
uint16_t presence_detect_state : 1;
|
||||
uint16_t electromechanical_lock_engaged : 1;
|
||||
uint16_t datalink_state_changed : 1;
|
||||
uint16_t rsvd : 7;
|
||||
} bit_field;
|
||||
uint16_t as_uint16_t;
|
||||
} express_slot_status_register;
|
||||
|
||||
typedef union _EXPRESS_ROOT_CONTROL_REGISTER {
|
||||
struct {
|
||||
uint16_t CorrectableSerrEnable : 1;
|
||||
uint16_t NonFatalSerrEnable : 1;
|
||||
uint16_t FatalSerrEnable : 1;
|
||||
uint16_t PMEInterruptEnable : 1;
|
||||
uint16_t CRSSoftwareVisibilityEnable : 1;
|
||||
uint16_t Rsvd : 11;
|
||||
} bit_field;
|
||||
uint16_t as_uint16_t;
|
||||
} express_root_control_register;
|
||||
|
||||
|
||||
typedef struct _PciExpressCap {
|
||||
uint8_t capid;
|
||||
uint8_t next_cap;
|
||||
express_capability_register express_cap_register;
|
||||
uint32_t device_cap;
|
||||
uint16_t device_control;
|
||||
uint16_t device_status;
|
||||
uint32_t link_cap;
|
||||
uint16_t link_control;
|
||||
uint16_t link_status;
|
||||
express_slot_capabiliies_register slot_cap;
|
||||
express_slot_control_register slot_control;
|
||||
express_slot_status_register slot_status;
|
||||
uint32_t root_status;
|
||||
uint32_t deviceCap2;
|
||||
uint16_t deviceControl2;
|
||||
uint16_t deviceStatus2;
|
||||
uint32_t linkCap2;
|
||||
uint16_t linkControl2;
|
||||
uint16_t linkStatus2;
|
||||
uint32_t slotCap2;
|
||||
uint16_t slotControl2;
|
||||
uint16_t slotStatus2;
|
||||
} pci_express_cap;
|
||||
|
||||
typedef struct _PciMsixCap {
|
||||
uint8_t cap_idd;
|
||||
uint8_t next_cap;
|
||||
uint16_t msg_control_reg;
|
||||
uint32_t msix_table_offset;
|
||||
uint32_t pba_offset;
|
||||
} pci_msix_cap;
|
||||
|
||||
typedef struct _PciHeader {
|
||||
union {
|
||||
pci_header_common common;
|
||||
pci_header_zero zero;
|
||||
pci_header_one one;
|
||||
};
|
||||
} pci_header;
|
||||
|
||||
struct pci_bars {
|
||||
uint64_t vaddr;
|
||||
uint64_t start;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
#endif /* VMD_SPEC_H */
|
801
lib/vmd/vmdpci.c
Normal file
801
lib/vmd/vmdpci.c
Normal file
@ -0,0 +1,801 @@
|
||||
/*-
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright (c) Intel Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "vmdpci.h"
|
||||
|
||||
static unsigned char *device_type[] = {
|
||||
"PCI Express Endpoint",
|
||||
"Legacy PCI Express Endpoint",
|
||||
"Reserved 1",
|
||||
"Reserved 2",
|
||||
"Root Port of PCI Express Root Complex",
|
||||
"Upstream Port of PCI Express Switch",
|
||||
"Downstream Port of PCI Express Switch",
|
||||
"PCI Express to PCI/PCI-X Bridge",
|
||||
"PCI/PCI-X to PCI Express Bridge",
|
||||
"Root Complex Integrated Endpoint",
|
||||
"Root Complex Event Collector",
|
||||
"Reserved Capability"
|
||||
};
|
||||
|
||||
static int g_end_device_count;
|
||||
|
||||
static bool
|
||||
vmd_is_valid_cfg_addr(vmd_pci_bus *bus, uint64_t addr)
|
||||
{
|
||||
if (bus == NULL || !addr || bus->vmd == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return addr >= (uint64_t)bus->vmd->cfg_vaddr &&
|
||||
addr < bus->vmd->cfgbar_size + (uint64_t)bus->vmd->cfg_vaddr;
|
||||
}
|
||||
|
||||
static void
|
||||
vmd_align_base_addrs(vmd_adapter *vmd, vmd_pci_device *dev, uint32_t alignment)
|
||||
{
|
||||
/*
|
||||
* Device is not in hot plug path, align the base address remaining from membar 1.
|
||||
*/
|
||||
if (vmd) {
|
||||
if (vmd->physical_addr & (alignment - 1)) {
|
||||
uint32_t pad = alignment - (vmd->physical_addr & (alignment - 1));
|
||||
vmd->physical_addr += pad;
|
||||
vmd->current_addr_size -= pad;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocates an address from vmd membar for the input memory size
|
||||
* vmdAdapter - vmd adapter object
|
||||
* dev - vmd_pci_device to allocate a base address for.
|
||||
* size - size of the memory window requested.
|
||||
* Size must be an integral multiple of 2. Addresses are returned on the size boundary.
|
||||
* Returns physical address within the VMD membar window, or 0x0 if cannot allocate window.
|
||||
* Consider increasing the size of vmd membar if 0x0 is returned.
|
||||
*/
|
||||
static uint64_t
|
||||
vmd_allocate_base_addr(vmd_adapter *vmd, vmd_pci_device *dev, uint32_t size)
|
||||
{
|
||||
uint64_t base_address = 0;
|
||||
|
||||
if (size && ((size & (~size + 1)) != size)) {
|
||||
return base_address;
|
||||
}
|
||||
|
||||
/*
|
||||
* If device is downstream of a hot plug port, allocate address from the
|
||||
* range dedicated for the hot plug slot. Search the list of addresses allocated to determine
|
||||
* if a free range exists that satisfy the input request. If a free range cannot be found,
|
||||
* get a buffer from the unused chunk. First fit algorithm, is used.
|
||||
*/
|
||||
if (dev) {
|
||||
vmd_pci_bus *hp_bus = vmd_is_dev_in_hotplug_path(dev);
|
||||
if (hp_bus && hp_bus->self) {
|
||||
return vmd_hp_allocate_base_addr(hp_bus->self->hp, size);
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure physical membar allocated is size aligned */
|
||||
if (vmd->physical_addr & (size - 1)) {
|
||||
uint32_t pad = size - (vmd->physical_addr & (size - 1));
|
||||
vmd->physical_addr += pad;
|
||||
vmd->current_addr_size -= pad;
|
||||
}
|
||||
|
||||
/* Allocate from membar if enough memory is left */
|
||||
if (vmd->current_addr_size >= size) {
|
||||
base_address = vmd->physical_addr;
|
||||
vmd->physical_addr += size;
|
||||
vmd->current_addr_size -= size;
|
||||
}
|
||||
|
||||
printf("%s: allocated(size) %lx (%x)\n", __func__, base_address, size);
|
||||
|
||||
return base_address;
|
||||
}
|
||||
|
||||
static bool
|
||||
vmd_is_end_device(vmd_pci_device *dev)
|
||||
{
|
||||
return (dev && dev->header) &&
|
||||
((dev->header->common.header_type & ~PCI_MULTI_FUNCTION) == PCI_HEADER_TYPE_NORMAL);
|
||||
}
|
||||
|
||||
static void
|
||||
vmd_update_base_limit_register(vmd_pci_device *dev, uint16_t base, uint16_t limit)
|
||||
{
|
||||
if (base && limit && dev != NULL) {
|
||||
vmd_pci_bus *bus = dev->parent;
|
||||
while (bus && bus->self != NULL) {
|
||||
vmd_pci_device *bridge = bus->self;
|
||||
|
||||
/* This is only for 32-bit memory space, need to revisit to support 64-bit */
|
||||
if (bridge->header->one.mem_base > base) {
|
||||
bridge->header->one.mem_base = base;
|
||||
base = bridge->header->one.mem_base;
|
||||
}
|
||||
|
||||
if (bridge->header->one.mem_limit < limit) {
|
||||
bridge->header->one.mem_limit = limit;
|
||||
limit = bridge->header->one.mem_limit;
|
||||
}
|
||||
bus = bus->parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
vmd_assign_base_addrs(vmd_pci_device *dev)
|
||||
{
|
||||
uint16_t mem_base = 0, mem_limit = 0;
|
||||
unsigned char mem_attr = 0;
|
||||
int last = dev->header_type ? 2 : 6;
|
||||
vmd_adapter *vmd = NULL;
|
||||
bool ret_val = false;
|
||||
uint32_t bar_value;
|
||||
|
||||
if (dev && dev->bus) {
|
||||
vmd = dev->bus->vmd;
|
||||
}
|
||||
|
||||
if (!vmd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
vmd_align_base_addrs(vmd, vmd->is_hotplug_scan ? dev : NULL, ONE_MB);
|
||||
|
||||
for (int i = 0; i < last; i++) {
|
||||
bar_value = dev->header->zero.BAR[i];
|
||||
dev->header->zero.BAR[i] = ~(0U);
|
||||
dev->bar[i].size = dev->header->zero.BAR[i];
|
||||
dev->header->zero.BAR[i] = bar_value;
|
||||
|
||||
if (dev->bar[i].size == ~(0U) || dev->bar[i].size == 0 ||
|
||||
dev->header->zero.BAR[i] & 1) {
|
||||
dev->bar[i].size = 0;
|
||||
continue;
|
||||
}
|
||||
mem_attr = dev->bar[i].size & PCI_BASE_ADDR_MASK;
|
||||
dev->bar[i].size = TWOS_COMPLEMENT(dev->bar[i].size & PCI_BASE_ADDR_MASK);
|
||||
dev->bar[i].start = vmd_allocate_base_addr(vmd, dev, dev->bar[i].size);
|
||||
dev->header->zero.BAR[i] = (uint32_t)dev->bar[i].start;
|
||||
|
||||
if (!dev->bar[i].start) {
|
||||
if (mem_attr == (PCI_BAR_MEMORY_PREFETCH | PCI_BAR_MEMORY_TYPE_64)) { i++; }
|
||||
continue;
|
||||
}
|
||||
|
||||
dev->bar[i].vaddr = ((uint64_t)vmd->mem_vaddr + (dev->bar[i].start - vmd->membar));
|
||||
mem_limit = BRIDGE_BASEREG(dev->header->zero.BAR[i]) +
|
||||
BRIDGE_BASEREG(dev->bar[i].size - 1);
|
||||
if (!mem_base) {
|
||||
mem_base = BRIDGE_BASEREG(dev->header->zero.BAR[i]);
|
||||
}
|
||||
|
||||
ret_val = true;
|
||||
|
||||
if (mem_attr == (PCI_BAR_MEMORY_PREFETCH | PCI_BAR_MEMORY_TYPE_64)) {
|
||||
i++;
|
||||
if (i < last) {
|
||||
dev->header->zero.BAR[i] = (uint32_t)(dev->bar[i].start >> PCI_DWORD_SHIFT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable device MEM and bus mastering */
|
||||
dev->header->zero.command |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
||||
uint16_t cmd = dev->header->zero.command;
|
||||
cmd++;
|
||||
|
||||
if (dev->msix_cap && ret_val) {
|
||||
uint32_t table_offset = ((volatile pci_msix_cap *)dev->msix_cap)->msix_table_offset;
|
||||
if (dev->bar[table_offset & 0x3].vaddr) {
|
||||
dev->msix_table = (volatile pci_msix_table_entry *)
|
||||
(dev->bar[table_offset & 0x3].vaddr + (table_offset & 0xfff8));
|
||||
}
|
||||
}
|
||||
|
||||
if (ret_val && vmd_is_end_device(dev)) {
|
||||
vmd_update_base_limit_register(dev, mem_base, mem_limit);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static void
|
||||
vmd_get_device_capabilities(vmd_pci_device *dev)
|
||||
|
||||
{
|
||||
volatile uint8_t *config_space;
|
||||
uint8_t capabilities_offset;
|
||||
pci_capabilities_header *capabilities_hdr;
|
||||
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
|
||||
config_space = (volatile uint8_t *)dev->header;
|
||||
if ((dev->header->common.status & PCI_CAPABILITIES_LIST) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
capabilities_offset = dev->header->zero.cap_pointer;
|
||||
if (dev->header->common.header_type & PCI_HEADER_TYPE_BRIDGE) {
|
||||
capabilities_offset = dev->header->one.cap_pointer;
|
||||
}
|
||||
|
||||
while (capabilities_offset > 0) {
|
||||
capabilities_hdr = (pci_capabilities_header *)&config_space[capabilities_offset];
|
||||
switch (capabilities_hdr->capability_id) {
|
||||
case CAPABILITY_ID_PCI_EXPRESS:
|
||||
dev->pcie_cap = (volatile pci_express_cap *)(capabilities_hdr);
|
||||
break;
|
||||
|
||||
case CAPABILITY_ID_MSI:
|
||||
dev->msi_cap = (volatile pci_msi_cap *)capabilities_hdr;
|
||||
break;
|
||||
|
||||
case CAPABILITY_ID_MSIX:
|
||||
dev->msix_cap = (volatile pci_msix_capability *)capabilities_hdr;
|
||||
dev->msix_table_size = dev->msix_cap->message_control.bit.table_size + 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
capabilities_offset = capabilities_hdr->next;
|
||||
}
|
||||
}
|
||||
|
||||
static volatile pci_enhanced_capability_header *
|
||||
vmd_get_enhanced_capabilities(vmd_pci_device *dev, uint16_t capability_id)
|
||||
{
|
||||
uint8_t *data;
|
||||
uint16_t cap_offset = EXTENDED_CAPABILITY_OFFSET;
|
||||
volatile pci_enhanced_capability_header *cap_hdr = NULL;
|
||||
|
||||
data = (uint8_t *)dev->header;
|
||||
while (cap_offset >= EXTENDED_CAPABILITY_OFFSET) {
|
||||
cap_hdr = (volatile pci_enhanced_capability_header *) &data[cap_offset];
|
||||
if (cap_hdr->capability_id == capability_id) {
|
||||
return cap_hdr;
|
||||
}
|
||||
cap_offset = cap_hdr->next;
|
||||
if (cap_offset == 0 || cap_offset < EXTENDED_CAPABILITY_OFFSET) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
vmd_read_config_space(vmd_pci_device *dev)
|
||||
{
|
||||
/*
|
||||
* Writes to the pci config space is posted weite. To ensure transaction reaches its destination
|
||||
* before another write is posed, an immediate read of the written value should be performed.
|
||||
*/
|
||||
dev->header->common.command |= (BUS_MASTER_ENABLE | MEMORY_SPACE_ENABLE);
|
||||
{ uint16_t cmd = dev->header->common.command; (void)cmd; }
|
||||
|
||||
vmd_get_device_capabilities(dev);
|
||||
dev->sn_cap = (serial_number_capability *)vmd_get_enhanced_capabilities(dev,
|
||||
DEVICE_SERIAL_NUMBER_CAP_ID);
|
||||
}
|
||||
|
||||
static vmd_pci_device *
|
||||
vmd_alloc_dev(vmd_pci_bus *bus, uint32_t devfn)
|
||||
{
|
||||
vmd_pci_device *dev = NULL;
|
||||
pci_header volatile *header;
|
||||
uint8_t header_type;
|
||||
uint32_t rev_class;
|
||||
|
||||
if (bus == NULL || bus->vmd == NULL) {
|
||||
return dev;
|
||||
}
|
||||
|
||||
header = (pci_header * volatile)(bus->vmd->cfg_vaddr +
|
||||
CONFIG_OFFSET_ADDR(bus->bus_number, devfn, 0, 0));
|
||||
if (!vmd_is_valid_cfg_addr(bus, (uint64_t)header)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (header->common.vendor_id == PCI_INVALID_VENDORID || header->common.vendor_id == 0x0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
printf(" *** PCI DEVICE FOUND : %04x:%04x ***\n",
|
||||
header->common.vendor_id, header->common.device_id);
|
||||
if ((dev = calloc(1, sizeof(vmd_pci_device))) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev->header = header;
|
||||
dev->vid = dev->header->common.vendor_id;
|
||||
dev->did = dev->header->common.device_id;
|
||||
dev->bus = bus;
|
||||
dev->parent = bus;
|
||||
dev->devfn = devfn;
|
||||
header_type = dev->header->common.header_type;
|
||||
rev_class = dev->header->common.rev_class;
|
||||
dev->class = rev_class >> 8;
|
||||
dev->header_type = header_type & 0x7;
|
||||
|
||||
if (header_type == PCI_HEADER_TYPE_BRIDGE) {
|
||||
dev->header->one.mem_base = 0xfff0;
|
||||
dev->header->one.mem_limit = 0x0;
|
||||
dev->header->one.prefetch_base_upper = 0x0;
|
||||
dev->header->one.prefetch_limit_upper = 0x0;
|
||||
dev->header->one.io_base_upper = 0x0;
|
||||
dev->header->one.io_limit_upper = 0x0;
|
||||
dev->header->one.primary = 0;
|
||||
dev->header->one.secondary = 0;
|
||||
dev->header->one.subordinate = 0;
|
||||
}
|
||||
|
||||
vmd_read_config_space(dev);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void
|
||||
vmd_add_bus_to_list(vmd_adapter *vmd, vmd_pci_bus *bus)
|
||||
{
|
||||
vmd_pci_bus *blist;
|
||||
|
||||
if (!bus || !vmd) {
|
||||
return;
|
||||
}
|
||||
|
||||
blist = vmd->bus_list;
|
||||
bus->next = NULL;
|
||||
if (blist == NULL) {
|
||||
vmd->bus_list = bus;
|
||||
return;
|
||||
}
|
||||
|
||||
while (blist->next != NULL) {
|
||||
blist = blist->next;
|
||||
}
|
||||
|
||||
blist->next = bus;
|
||||
}
|
||||
|
||||
static void
|
||||
vmd_pcibus_remove_device(vmd_pci_bus *bus, vmd_pci_device *device)
|
||||
{
|
||||
vmd_pci_device *list = bus->dev_list;
|
||||
|
||||
if (list == device) {
|
||||
bus->dev_list = NULL;
|
||||
}
|
||||
|
||||
while (list->next != NULL) {
|
||||
if (list->next == device) {
|
||||
list->next = list->next->next;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
vmd_bus_add_device(vmd_pci_bus *bus, vmd_pci_device *device)
|
||||
{
|
||||
if (!bus || !device) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
vmd_pci_device *next_dev = bus->dev_list;
|
||||
device->next = NULL;
|
||||
if (next_dev == NULL) {
|
||||
bus->dev_list = device;
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (next_dev->next != NULL) {
|
||||
next_dev = next_dev->next;
|
||||
}
|
||||
|
||||
next_dev->next = device;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static vmd_pci_bus *
|
||||
vmd_create_new_bus(vmd_pci_bus *parent, vmd_pci_device *bridge, uint8_t bus_number)
|
||||
{
|
||||
vmd_pci_bus *new_bus;
|
||||
if (!parent) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new_bus = (vmd_pci_bus *)calloc(1, sizeof(vmd_pci_bus));
|
||||
if (!new_bus) {
|
||||
return NULL;
|
||||
}
|
||||
new_bus->parent = parent;
|
||||
new_bus->domain = parent->domain;
|
||||
new_bus->bus_number = bus_number;
|
||||
new_bus->secondary_bus = new_bus->subordinate_bus = bus_number;
|
||||
new_bus->self = bridge;
|
||||
new_bus->vmd = parent->vmd;
|
||||
bridge->subordinate = new_bus;
|
||||
|
||||
bridge->pci.addr.bus = new_bus->bus_number;
|
||||
bridge->pci.addr.dev = bridge->devfn;
|
||||
bridge->pci.addr.func = 0;
|
||||
bridge->pci.addr.domain = parent->vmd->pci.addr.domain;
|
||||
|
||||
return new_bus;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assigns a bus number from the list of available
|
||||
* bus numbers. If the device is downstream of a hot plug port,
|
||||
* assign the bus number from thiose assigned to the HP port. Otherwise,
|
||||
* assign the next bus number from the vmd bus number list.
|
||||
*/
|
||||
static uint8_t
|
||||
vmd_get_next_bus_number(vmd_pci_device *dev, vmd_adapter *vmd)
|
||||
{
|
||||
uint8_t bus = 0xff;
|
||||
|
||||
if (dev) {
|
||||
vmd_pci_bus *hp_bus = vmd_is_dev_in_hotplug_path(dev);
|
||||
if (hp_bus && hp_bus->self && hp_bus->self->hp) {
|
||||
return vmd_hp_get_next_bus_number(hp_bus->self->hp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Device is not under a hot plug path. Return next global bus number */
|
||||
if ((vmd->next_bus_number + 1) < vmd->max_pci_bus) {
|
||||
bus = vmd->next_bus_number;
|
||||
vmd->next_bus_number++;
|
||||
}
|
||||
return bus;
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
vmd_get_hotplug_bus_numbers(vmd_pci_device *dev)
|
||||
{
|
||||
uint8_t bus_number = 0xff;
|
||||
|
||||
if (dev && dev->bus && dev->bus->vmd &&
|
||||
((dev->bus->vmd->next_bus_number + RESERVED_HOTPLUG_BUSES) < dev->bus->vmd->max_pci_bus)) {
|
||||
bus_number = RESERVED_HOTPLUG_BUSES;
|
||||
dev->bus->vmd->next_bus_number += RESERVED_HOTPLUG_BUSES;
|
||||
}
|
||||
|
||||
return bus_number;
|
||||
}
|
||||
|
||||
static void
|
||||
vmd_enable_msix(vmd_pci_device *dev)
|
||||
{
|
||||
volatile uint16_t control;
|
||||
|
||||
if (!(dev && dev->msix_cap)) {
|
||||
return;
|
||||
}
|
||||
|
||||
control = dev->msix_cap->message_control.as_uint16_t | (1 << 14);
|
||||
dev->msix_cap->message_control.as_uint16_t = control;
|
||||
control = dev->msix_cap->message_control.as_uint16_t;
|
||||
dev->msix_cap->message_control.as_uint16_t = (control | (1 << 15));
|
||||
control = dev->msix_cap->message_control.as_uint16_t;
|
||||
control = control & ~(1 << 14);
|
||||
dev->msix_cap->message_control.as_uint16_t = control;
|
||||
control = dev->msix_cap->message_control.as_uint16_t;
|
||||
}
|
||||
|
||||
static void
|
||||
vmd_disable_msix(vmd_pci_device *dev)
|
||||
{
|
||||
volatile uint16_t control;
|
||||
|
||||
if (!(dev && dev->msix_cap)) {
|
||||
return;
|
||||
}
|
||||
|
||||
control = dev->msix_cap->message_control.as_uint16_t | (1 << 14);
|
||||
dev->msix_cap->message_control.as_uint16_t = control;
|
||||
control = dev->msix_cap->message_control.as_uint16_t & ~(1 << 15);
|
||||
dev->msix_cap->message_control.as_uint16_t = control;
|
||||
control = dev->msix_cap->message_control.as_uint16_t;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up MSI-X table entries for the port. Vmd MSIX vector 0 is used for
|
||||
* port interrupt, so vector 0 is mapped to all MSIX entries for the port.
|
||||
*/
|
||||
static void
|
||||
vmd_setup_msix(vmd_pci_device *dev, volatile struct _pci_misx_table_entry *vmdEntry)
|
||||
{
|
||||
int entry;
|
||||
|
||||
if (!dev || !vmdEntry || !dev->msix_cap) {
|
||||
return;
|
||||
}
|
||||
|
||||
vmd_disable_msix(dev);
|
||||
if (dev->msix_table == NULL || dev->msix_table_size > MAX_MSIX_TABLE_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (entry = 0; entry < dev->msix_table_size; ++entry) {
|
||||
dev->msix_table[entry].vector_control = 1;
|
||||
}
|
||||
vmd_enable_msix(dev);
|
||||
}
|
||||
|
||||
static void
|
||||
vmd_bus_update_bridge_info(vmd_pci_device *bridge)
|
||||
{
|
||||
/* Update the subordinate bus of all bridges above this bridge */
|
||||
volatile vmd_pci_device *dev = bridge;
|
||||
uint8_t subordinate_bus;
|
||||
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
subordinate_bus = bridge->header->one.subordinate;
|
||||
while (dev->parent_bridge != NULL) {
|
||||
dev = dev->parent_bridge;
|
||||
if (dev->header->one.subordinate < subordinate_bus) {
|
||||
dev->header->one.subordinate = subordinate_bus;
|
||||
subordinate_bus = dev->header->one.subordinate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Scans a single bus for all devices attached and return a count of
|
||||
* how many devices found. In the VMD topology, it is assume there are no multi-
|
||||
* function devices. Hence a bus(bridge) will not have multi function with both type
|
||||
* 0 and 1 header.
|
||||
*
|
||||
* The other option for implementing this function is the bus is an int and
|
||||
* create a new device PciBridge. PciBridge would inherit from PciDevice with extra fields,
|
||||
* sub/pri/sec bus. The input becomes PciPort, bus number and parent_bridge.
|
||||
*
|
||||
* The bus number is scanned and if a device is found, based on the header_type, create
|
||||
* either PciBridge(1) or PciDevice(0).
|
||||
*
|
||||
* If a PciBridge, assign bus numbers and rescan new bus. The currenty PciBridge being
|
||||
* scanned becomes the passed in parent_bridge with the new bus number.
|
||||
*
|
||||
* The linked list becomes list of pciBridges with PciDevices attached.
|
||||
*
|
||||
* Return count of how many devices found(type1 + type 0 header devices)
|
||||
*/
|
||||
static uint8_t
|
||||
vmd_scan_single_bus(vmd_pci_bus *bus, vmd_pci_device *parent_bridge)
|
||||
{
|
||||
/* assuming only single function devices are on the bus */
|
||||
vmd_pci_device *new_dev;
|
||||
vmd_pci_bus *new_bus;
|
||||
int device_number, dev_cnt = 0;
|
||||
express_slot_capabiliies_register slot_cap;
|
||||
uint8_t new_bus_num;
|
||||
|
||||
if (bus == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (device_number = 0; device_number < 32; device_number++) {
|
||||
new_dev = vmd_alloc_dev(bus, device_number);
|
||||
if (new_dev == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dev_cnt++;
|
||||
if (new_dev->header->common.header_type & PCI_HEADER_TYPE_BRIDGE) {
|
||||
slot_cap.as_uint32_t = 0;
|
||||
if (new_dev->pcie_cap != NULL) {
|
||||
slot_cap.as_uint32_t = new_dev->pcie_cap->slot_cap.as_uint32_t;
|
||||
}
|
||||
|
||||
new_bus_num = vmd_get_next_bus_number(bus->vmd->is_hotplug_scan ? new_dev : NULL, bus->vmd);
|
||||
if (new_bus_num == 0xff) {
|
||||
free(new_dev);
|
||||
return dev_cnt;
|
||||
}
|
||||
new_bus = vmd_create_new_bus(bus, new_dev, new_bus_num);
|
||||
if (!new_bus) {
|
||||
free(new_dev);
|
||||
return dev_cnt;
|
||||
}
|
||||
new_bus->primary_bus = bus->secondary_bus;
|
||||
new_bus->self = new_dev;
|
||||
new_dev->bus_object = new_bus;
|
||||
|
||||
if (slot_cap.bit_field.hotplug_capable) {
|
||||
new_bus->hotplug_buses = vmd_get_hotplug_bus_numbers(new_dev);
|
||||
new_bus->subordinate_bus += new_bus->hotplug_buses;
|
||||
}
|
||||
new_dev->parent_bridge = parent_bridge;
|
||||
new_dev->header->one.primary = new_bus->primary_bus;
|
||||
new_dev->header->one.secondary = new_bus->secondary_bus;
|
||||
new_dev->header->one.subordinate = new_bus->subordinate_bus;
|
||||
|
||||
vmd_bus_update_bridge_info(new_dev);
|
||||
vmd_add_bus_to_list(bus->vmd, new_bus);
|
||||
|
||||
/* Attach hot plug instance if HP is supported */
|
||||
if (slot_cap.bit_field.hotplug_capable) {
|
||||
new_dev->hp = vmd_new_hotplug(new_bus, new_bus->hotplug_buses);
|
||||
}
|
||||
|
||||
vmd_dev_init(new_dev);
|
||||
|
||||
dev_cnt += vmd_scan_single_bus(new_bus, new_dev);
|
||||
if (new_dev->pcie_cap != NULL) {
|
||||
if (new_dev->pcie_cap->express_cap_register.bit_field.device_type == SwitchUpstreamPort) {
|
||||
return dev_cnt;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Attach the device to the current bus and assign base addresses */
|
||||
vmd_bus_add_device(bus, new_dev);
|
||||
g_end_device_count++;
|
||||
if (vmd_assign_base_addrs(new_dev)) {
|
||||
vmd_setup_msix(new_dev, &bus->vmd->msix_table[0]);
|
||||
vmd_dev_init(new_dev);
|
||||
if (vmd_is_supported_device(new_dev)) {
|
||||
vmd_adapter *vmd = bus->vmd;
|
||||
vmd->target[vmd->nvme_count] = new_dev;
|
||||
vmd->nvme_count++;
|
||||
}
|
||||
} else {
|
||||
printf("%s: Removing failed device: %p\n", __func__, new_dev);
|
||||
vmd_pcibus_remove_device(bus, new_dev);
|
||||
if (dev_cnt) {
|
||||
dev_cnt--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dev_cnt;
|
||||
}
|
||||
|
||||
static void
|
||||
vmd_print_pci_info(vmd_pci_device *dev)
|
||||
{
|
||||
if (dev == NULL) {
|
||||
return;
|
||||
}
|
||||
if (dev->header == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev->pcie_cap != NULL) {
|
||||
printf("PCI DEVICE: [%04X:%04X] type(%x) : %s\n",
|
||||
dev->header->common.vendor_id, dev->header->common.device_id,
|
||||
dev->pcie_cap->express_cap_register.bit_field.device_type,
|
||||
device_type[dev->pcie_cap->express_cap_register.bit_field.device_type]);
|
||||
} else {
|
||||
printf("PCI DEVICE: [%04X:%04X]\n", dev->header->common.vendor_id, dev->header->common.device_id);
|
||||
}
|
||||
|
||||
printf(" DOMAIN:BDF: %04x:%02x:%02x:%x\n", dev->pci.addr.domain,
|
||||
dev->pci.addr.bus, dev->pci.addr.dev, dev->pci.addr.func);
|
||||
|
||||
if (!(dev->header_type & PCI_HEADER_TYPE_BRIDGE) && dev->bus) {
|
||||
printf(" base addr: %x : %p\n", dev->header->zero.BAR[0], (void *)dev->bar[0].vaddr);
|
||||
}
|
||||
|
||||
if ((dev->header_type & PCI_HEADER_TYPE_BRIDGE)) {
|
||||
printf(" Primary = %d, Secondary = %d, Subordinate = %d\n",
|
||||
dev->header->one.primary, dev->header->one.secondary, dev->header->one.subordinate);
|
||||
if (dev->pcie_cap && dev->pcie_cap->express_cap_register.bit_field.slot_implemented) {
|
||||
printf(" Slot implemented on this device.\n");
|
||||
if (dev->pcie_cap->slot_cap.bit_field.hotplug_capable) {
|
||||
printf("Device has HOT-PLUG capable slot.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->sn_cap != NULL) {
|
||||
uint8_t *snLow = (uint8_t *)&dev->sn_cap->sn_low;
|
||||
uint8_t *snHi = (uint8_t *)&dev->sn_cap->sn_hi;
|
||||
printf(" SN: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n",
|
||||
snHi[3], snHi[2], snHi[1], snHi[0], snLow[3], snLow[2], snLow[1], snLow[0]);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
vmd_pci_print(vmd_pci_bus *bus_list)
|
||||
{
|
||||
vmd_pci_bus *bus = bus_list;
|
||||
|
||||
printf("\n ...PCIE devices attached to VMD %04x:%02x:%02x:%x...\n",
|
||||
bus_list->vmd->pci.addr.domain, bus_list->vmd->pci.addr.bus,
|
||||
bus_list->vmd->pci.addr.dev, bus_list->vmd->pci.addr.func);
|
||||
printf("----------------------------------------------\n");
|
||||
|
||||
while (bus != NULL) {
|
||||
vmd_print_pci_info(bus->self);
|
||||
vmd_pci_device *dev = bus->dev_list;
|
||||
while (dev != NULL) {
|
||||
vmd_print_pci_info(dev);
|
||||
dev = dev->next;
|
||||
}
|
||||
bus = bus->next;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t
|
||||
vmd_scan_pcibus(vmd_pci_bus *bus)
|
||||
{
|
||||
vmd_pci_bus *new_bus = bus;
|
||||
int dev_cnt;
|
||||
|
||||
if (bus == NULL || bus->vmd == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_end_device_count = 0;
|
||||
vmd_add_bus_to_list(bus->vmd, new_bus);
|
||||
bus->vmd->next_bus_number = bus->bus_number + 1;
|
||||
dev_cnt = vmd_scan_single_bus(new_bus, NULL);
|
||||
printf(" **** VMD scan found %d devices\n", dev_cnt);
|
||||
printf(" VMD scan found %d END DEVICES\n", g_end_device_count);
|
||||
vmd_pci_print(bus->vmd->bus_list);
|
||||
return (uint8_t)dev_cnt;
|
||||
}
|
||||
|
||||
bool
|
||||
vmd_is_supported_device(vmd_pci_device *dev)
|
||||
{
|
||||
bool isSupported = false;
|
||||
|
||||
if (dev && dev->header
|
||||
&& (dev->class == PCI_CLASS_STORAGE_EXPRESS)
|
||||
#ifndef SUPPORT_ALL_SSDS
|
||||
&& (dev->header->common.vendor_id == 0x8086)
|
||||
#endif
|
||||
) {
|
||||
isSupported = true;
|
||||
}
|
||||
return isSupported;
|
||||
}
|
||||
|
||||
SPDK_LOG_REGISTER_COMPONENT("vmd_pci", SPDK_LOG_VMD_PCI)
|
204
lib/vmd/vmdpci.h
Normal file
204
lib/vmd/vmdpci.h
Normal file
@ -0,0 +1,204 @@
|
||||
/*-
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright (c) Intel Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Intel Corporation nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef VMD_PCI_H
|
||||
#define VMD_PCI_H
|
||||
|
||||
#include "spdk/stdinc.h"
|
||||
#include "spdk/vmd.h"
|
||||
#include "spdk/env.h"
|
||||
#include "spdk/util.h"
|
||||
#include "spdk_internal/log.h"
|
||||
#include "vmd_spec.h"
|
||||
|
||||
struct vmd_hot_plug;
|
||||
struct vmd_adapter;
|
||||
struct vmd_pci_device;
|
||||
|
||||
typedef struct vmd_pci_bus {
|
||||
struct vmd_adapter *vmd;
|
||||
struct vmd_pci_bus *parent; /* parent bus that this bus is attached to(primary bus. */
|
||||
struct vmd_pci_device *self; /* Pci device that describes this bus(bar, bus numbers, etc */
|
||||
|
||||
uint32_t domain : 8;
|
||||
uint32_t hotplug_buses : 10;
|
||||
uint32_t is_added : 1;
|
||||
uint32_t hp_event_queued : 1;
|
||||
uint32_t rsv : 12;
|
||||
|
||||
uint32_t bus_number : 8;
|
||||
uint32_t primary_bus : 8;
|
||||
uint32_t secondary_bus : 8;
|
||||
uint32_t subordinate_bus : 8;
|
||||
|
||||
struct vmd_pci_device *dev_list; /* list of pci end device attached to this bus */
|
||||
struct vmd_pci_bus *next; /* link for all buses found during scan */
|
||||
} vmd_pci_bus;
|
||||
|
||||
typedef struct vmd_pci_device {
|
||||
struct spdk_pci_device pci;
|
||||
struct pci_bars bar[6];
|
||||
|
||||
struct vmd_pci_device *parent_bridge, *next;
|
||||
vmd_pci_bus *bus, *parent;
|
||||
vmd_pci_bus *bus_object; /* bus tracks pci bus associated with this dev if type 1 dev. */
|
||||
vmd_pci_bus *subordinate;
|
||||
volatile pci_header *header;
|
||||
volatile pci_express_cap *pcie_cap;
|
||||
volatile pci_msix_capability *msix_cap;
|
||||
volatile pci_msi_cap *msi_cap;
|
||||
volatile serial_number_capability *sn_cap;
|
||||
volatile pci_msix_table_entry *msix_table;
|
||||
|
||||
uint32_t class;
|
||||
uint16_t vid;
|
||||
uint16_t did;
|
||||
uint16_t pcie_flags, msix_table_size;
|
||||
uint32_t devfn;
|
||||
|
||||
uint32_t header_type : 1;
|
||||
uint32_t multifunction : 1;
|
||||
uint32_t hotplug_bridge : 1;
|
||||
uint32_t is_added : 1;
|
||||
uint32_t is_hooked : 1;
|
||||
uint32_t rsv1 : 12;
|
||||
uint32_t target : 16;
|
||||
|
||||
struct vmd_hot_plug *hp;
|
||||
} vmd_pci_device;
|
||||
|
||||
|
||||
/*
|
||||
* memory element for base address assignment and reuse
|
||||
*/
|
||||
struct pci_mem_mgr {
|
||||
uint32_t size : 30; /* size of memory element */
|
||||
uint32_t in_use : 1;
|
||||
uint32_t rsv : 1;
|
||||
uint64_t addr;
|
||||
};
|
||||
|
||||
typedef struct vmd_hot_plug {
|
||||
uint32_t count : 12;
|
||||
uint32_t reserved_bus_count : 4;
|
||||
uint32_t max_hotplug_bus_number : 8;
|
||||
uint32_t next_bus_number : 8;
|
||||
uint32_t addr_size;
|
||||
uint64_t physical_addr;
|
||||
express_slot_status_register slot_status;
|
||||
struct pci_mem_mgr mem[ADDR_ELEM_COUNT];
|
||||
uint8_t bus_numbers[RESERVED_HOTPLUG_BUSES];
|
||||
vmd_pci_bus *bus;
|
||||
} vmd_hot_plug;
|
||||
|
||||
/*
|
||||
* The VMD adapter
|
||||
*/
|
||||
typedef struct vmd_adapter {
|
||||
struct spdk_pci_device pci;
|
||||
uint32_t domain;
|
||||
/* physical and virtual VMD bars */
|
||||
uint64_t cfgbar, cfgbar_size;
|
||||
uint64_t membar, membar_size;
|
||||
uint64_t msixbar, msixbar_size;
|
||||
volatile uint8_t *cfg_vaddr;
|
||||
volatile uint8_t *mem_vaddr;
|
||||
volatile uint8_t *msix_vaddr;
|
||||
volatile struct _pci_misx_table_entry *msix_table;
|
||||
uint32_t bar_sizes[6];
|
||||
|
||||
uint64_t physical_addr;
|
||||
uint32_t current_addr_size;
|
||||
|
||||
uint32_t next_bus_number : 10;
|
||||
uint32_t max_pci_bus : 10;
|
||||
uint32_t is_hotplug_scan : 1;
|
||||
uint32_t is_ready : 1;
|
||||
uint32_t processing_hp : 1;
|
||||
uint32_t max_payload_size: 3;
|
||||
uint32_t rsv : 6;
|
||||
|
||||
/* end devices attached to vmd adapters */
|
||||
struct vmd_pci_device *target[MAX_VMD_TARGET];
|
||||
uint32_t dev_count : 16;
|
||||
uint32_t nvme_count : 8;
|
||||
uint32_t vmd_index : 8;
|
||||
|
||||
struct vmd_pci_bus vmd_bus, *bus_list;
|
||||
|
||||
struct event_fifo *hp_queue;
|
||||
|
||||
} vmd_adapter;
|
||||
|
||||
|
||||
/* TODO: Temporary stubs for Hot Plug interface */
|
||||
static inline vmd_pci_bus *
|
||||
vmd_is_dev_in_hotplug_path(vmd_pci_device *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
vmd_hp_allocate_base_addr(struct vmd_hot_plug *hp, uint32_t size)
|
||||
{
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
vmd_hp_enable_hotplug(struct vmd_hot_plug *hp)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static inline struct vmd_hot_plug *
|
||||
vmd_new_hotplug(vmd_pci_bus *newBus, uint8_t reservedBuses)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline uint8_t
|
||||
vmd_hp_get_next_bus_number(struct vmd_hot_plug *hp)
|
||||
{
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pci interface
|
||||
*/
|
||||
bool vmd_is_supported_device(vmd_pci_device *dev);
|
||||
uint8_t vmd_scan_pcibus(vmd_pci_bus *bus);
|
||||
void vmd_dev_init(vmd_pci_device *dev);
|
||||
|
||||
#endif /* VMD_PCI_H */
|
@ -32,7 +32,7 @@
|
||||
#
|
||||
|
||||
BLOCKDEV_MODULES_LIST = bdev_lvol blobfs blob blob_bdev lvol
|
||||
BLOCKDEV_MODULES_LIST += bdev_malloc bdev_null bdev_nvme nvme bdev_passthru bdev_error bdev_gpt bdev_split
|
||||
BLOCKDEV_MODULES_LIST += bdev_malloc bdev_null bdev_nvme nvme bdev_passthru bdev_error bdev_gpt bdev_split vmd
|
||||
BLOCKDEV_MODULES_LIST += bdev_raid
|
||||
|
||||
ifeq ($(CONFIG_CRYPTO),y)
|
||||
|
Loading…
x
Reference in New Issue
Block a user