From patchwork Mon Mar 2 08:29:27 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sheng Yang X-Patchwork-Id: 9500 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n228Tjvx011310 for ; Mon, 2 Mar 2009 08:29:45 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756230AbZCBI3m (ORCPT ); Mon, 2 Mar 2009 03:29:42 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1756252AbZCBI3l (ORCPT ); Mon, 2 Mar 2009 03:29:41 -0500 Received: from mga11.intel.com ([192.55.52.93]:33048 "EHLO mga11.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755465AbZCBI3j (ORCPT ); Mon, 2 Mar 2009 03:29:39 -0500 Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by fmsmga102.fm.intel.com with ESMTP; 02 Mar 2009 00:27:22 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.38,288,1233561600"; d="scan'208";a="435277309" Received: from syang10-desktop.sh.intel.com (HELO syang10-desktop) ([10.239.13.189]) by fmsmga002.fm.intel.com with ESMTP; 02 Mar 2009 00:25:23 -0800 Received: from yasker by syang10-desktop with local (Exim 4.69) (envelope-from ) id 1Le3Wo-0001oK-00; Mon, 02 Mar 2009 16:29:34 +0800 From: Sheng Yang To: Avi Kivity , Marcelo Tosatti , Anthony Liguori Cc: kvm@vger.kernel.org, Sheng Yang Subject: [PATCH 04/10] Support for device capability Date: Mon, 2 Mar 2009 16:29:27 +0800 Message-Id: <1235982573-6932-5-git-send-email-sheng@linux.intel.com> X-Mailer: git-send-email 1.5.6.3 In-Reply-To: <1235982573-6932-1-git-send-email-sheng@linux.intel.com> References: <1235982573-6932-1-git-send-email-sheng@linux.intel.com> Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org This framework can be easily extended to support device capability, like MSI/MSI-x. Signed-off-by: Sheng Yang --- qemu/hw/pci.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ qemu/hw/pci.h | 30 ++++++++++++++++++++ 2 files changed, 115 insertions(+), 0 deletions(-) diff --git a/qemu/hw/pci.c b/qemu/hw/pci.c index ccb4cfa..2225669 100644 --- a/qemu/hw/pci.c +++ b/qemu/hw/pci.c @@ -427,11 +427,65 @@ static void pci_update_mappings(PCIDevice *d) } } +int pci_access_cap_config(PCIDevice *pci_dev, uint32_t address, int len) +{ + if (pci_dev->cap.supported && address >= pci_dev->cap.start && + (address + len) < pci_dev->cap.start + pci_dev->cap.length) + return 1; + return 0; +} + +uint32_t pci_default_cap_read_config(PCIDevice *pci_dev, + uint32_t address, int len) +{ + uint32_t val = 0; + + if (pci_access_cap_config(pci_dev, address, len)) { + switch(len) { + default: + case 4: + if (address < pci_dev->cap.start + pci_dev->cap.length - 4) { + val = le32_to_cpu(*(uint32_t *)(pci_dev->cap.config + + address - pci_dev->cap.start)); + break; + } + /* fall through */ + case 2: + if (address < pci_dev->cap.start + pci_dev->cap.length - 2) { + val = le16_to_cpu(*(uint16_t *)(pci_dev->cap.config + + address - pci_dev->cap.start)); + break; + } + /* fall through */ + case 1: + val = pci_dev->cap.config[address - pci_dev->cap.start]; + break; + } + } + return val; +} + +void pci_default_cap_write_config(PCIDevice *pci_dev, + uint32_t address, uint32_t val, int len) +{ + if (pci_access_cap_config(pci_dev, address, len)) { + int i; + for (i = 0; i < len; i++) { + pci_dev->cap.config[address + i - pci_dev->cap.start] = val; + val >>= 8; + } + return; + } +} + uint32_t pci_default_read_config(PCIDevice *d, uint32_t address, int len) { uint32_t val; + if (pci_access_cap_config(d, address, len)) + return d->cap.config_read(d, address, len); + switch(len) { default: case 4: @@ -485,6 +539,11 @@ void pci_default_write_config(PCIDevice *d, return; } default_config: + if (pci_access_cap_config(d, address, len)) { + d->cap.config_write(d, address, val, len); + return; + } + /* not efficient, but simple */ addr = address; for(i = 0; i < len; i++) { @@ -903,3 +962,29 @@ PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint16_t vid, uint16_t did, s->bus = pci_register_secondary_bus(&s->dev, map_irq); return s->bus; } + +void pci_enable_capability_support(PCIDevice *pci_dev, + uint32_t config_start, + PCICapConfigReadFunc *config_read, + PCICapConfigWriteFunc *config_write, + PCICapConfigInitFunc *config_init) +{ + if (!pci_dev) + return; + + if (config_start >= 0x40 && config_start < 0xff) + pci_dev->cap.start = config_start; + else + pci_dev->cap.start = PCI_CAPABILITY_CONFIG_DEFAULT_START_ADDR; + if (config_read) + pci_dev->cap.config_read = config_read; + else + pci_dev->cap.config_read = pci_default_cap_read_config; + if (config_write) + pci_dev->cap.config_write = config_write; + else + pci_dev->cap.config_write = pci_default_cap_write_config; + pci_dev->cap.supported = 1; + pci_dev->config[0x34] = pci_dev->cap.start; + config_init(pci_dev); +} diff --git a/qemu/hw/pci.h b/qemu/hw/pci.h index 227da6e..7f3548b 100644 --- a/qemu/hw/pci.h +++ b/qemu/hw/pci.h @@ -133,6 +133,12 @@ typedef void PCIMapIORegionFunc(PCIDevice *pci_dev, int region_num, uint32_t addr, uint32_t size, int type); typedef int PCIUnregisterFunc(PCIDevice *pci_dev); +typedef void PCICapConfigWriteFunc(PCIDevice *pci_dev, + uint32_t address, uint32_t val, int len); +typedef uint32_t PCICapConfigReadFunc(PCIDevice *pci_dev, + uint32_t address, int len); +typedef void PCICapConfigInitFunc(PCIDevice *pci_dev); + #define PCI_ADDRESS_SPACE_MEM 0x00 #define PCI_ADDRESS_SPACE_IO 0x01 #define PCI_ADDRESS_SPACE_MEM_PREFETCH 0x08 @@ -183,6 +189,10 @@ typedef struct PCIIORegion { #define PCI_COMMAND_RESERVED_MASK_HI (PCI_COMMAND_RESERVED >> 8) +#define PCI_CAPABILITY_CONFIG_MAX_LENGTH 0x60 +#define PCI_CAPABILITY_CONFIG_DEFAULT_START_ADDR 0x40 +#define PCI_CAPABILITY_CONFIG_MSI_LENGTH 0x10 + struct PCIDevice { /* PCI config space */ uint8_t config[256]; @@ -205,6 +215,15 @@ struct PCIDevice { /* Current IRQ levels. Used internally by the generic PCI code. */ int irq_state[4]; + + /* Device capability configuration space */ + struct { + int supported; + uint8_t config[PCI_CAPABILITY_CONFIG_MAX_LENGTH]; + unsigned int start, length; + PCICapConfigReadFunc *config_read; + PCICapConfigWriteFunc *config_write; + } cap; }; PCIDevice *pci_register_device(PCIBus *bus, const char *name, @@ -217,6 +236,12 @@ void pci_register_io_region(PCIDevice *pci_dev, int region_num, uint32_t size, int type, PCIMapIORegionFunc *map_func); +void pci_enable_capability_support(PCIDevice *pci_dev, + uint32_t config_start, + PCICapConfigReadFunc *config_read, + PCICapConfigWriteFunc *config_write, + PCICapConfigInitFunc *config_init); + int pci_map_irq(PCIDevice *pci_dev, int pin); uint32_t pci_default_read_config(PCIDevice *d, uint32_t address, int len); @@ -224,6 +249,11 @@ void pci_default_write_config(PCIDevice *d, uint32_t address, uint32_t val, int len); void pci_device_save(PCIDevice *s, QEMUFile *f); int pci_device_load(PCIDevice *s, QEMUFile *f); +uint32_t pci_default_cap_read_config(PCIDevice *pci_dev, + uint32_t address, int len); +void pci_default_cap_write_config(PCIDevice *pci_dev, + uint32_t address, uint32_t val, int len); +int pci_access_cap_config(PCIDevice *pci_dev, uint32_t address, int len); typedef void (*pci_set_irq_fn)(qemu_irq *pic, int irq_num, int level); typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num);