|
| 1 | +/*- |
| 2 | +* Copyright (c) 2011 NetApp, Inc. |
| 3 | +* Copyright (c) 2018 Intel Corporation |
| 4 | +* All rights reserved. |
| 5 | +* |
| 6 | +* Redistribution and use in source and binary forms, with or without |
| 7 | +* modification, are permitted provided that the following conditions |
| 8 | +* are met: |
| 9 | +* 1. Redistributions of source code must retain the above copyright |
| 10 | +* notice, this list of conditions and the following disclaimer. |
| 11 | +* 2. Redistributions in binary form must reproduce the above copyright |
| 12 | +* notice, this list of conditions and the following disclaimer in the |
| 13 | +* documentation and/or other materials provided with the distribution. |
| 14 | +* |
| 15 | +* THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND |
| 16 | +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 17 | +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 18 | +* ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE |
| 19 | +* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 20 | +* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 21 | +* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 22 | +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 23 | +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 24 | +* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 25 | +* SUCH DAMAGE. |
| 26 | +* |
| 27 | +* $FreeBSD$ |
| 28 | +*/ |
| 29 | + |
| 30 | +#include <hypervisor.h> |
| 31 | +#include <hv_lib.h> |
| 32 | +#include <acrn_common.h> |
| 33 | +#include <hv_arch.h> |
| 34 | +#include <hv_debug.h> |
| 35 | +#include "pci_priv.h" |
| 36 | + |
| 37 | + |
| 38 | +static bool is_cfg_addr(uint16_t addr) |
| 39 | +{ |
| 40 | + return (addr >= PCI_CONFIG_ADDR) && (addr < (PCI_CONFIG_ADDR + 4)); |
| 41 | +} |
| 42 | + |
| 43 | +static bool is_cfg_data(uint16_t addr) |
| 44 | +{ |
| 45 | + return (addr >= PCI_CONFIG_DATA) && (addr < (PCI_CONFIG_DATA + 4)); |
| 46 | +} |
| 47 | + |
| 48 | +static void pci_cfg_clear_cache(struct pci_addr_info *pi) |
| 49 | +{ |
| 50 | + pi->cached_bdf = 0xffffU; |
| 51 | + pi->cached_reg = 0U; |
| 52 | + pi->cached_enable = 0U; |
| 53 | +} |
| 54 | + |
| 55 | +static uint32_t pci_cfg_io_read(__unused struct vm_io_handler *hdlr, |
| 56 | + struct vm *vm, uint16_t addr, size_t bytes) |
| 57 | +{ |
| 58 | + uint32_t val = 0xffffffffU; |
| 59 | + struct vpci *vpci = &vm->vpci; |
| 60 | + struct pci_addr_info *pi = &vpci->addr_info; |
| 61 | + |
| 62 | + if (is_cfg_addr(addr)) { |
| 63 | + /* TODO: handling the non 4 bytes access */ |
| 64 | + if (bytes == 4U) { |
| 65 | + val = (PCI_BUS(pi->cached_bdf) << 16) |
| 66 | + | (PCI_SLOT(pi->cached_bdf) << 11) |
| 67 | + | (PCI_FUNC(pi->cached_bdf) << 8) |
| 68 | + | pi->cached_reg; |
| 69 | + |
| 70 | + if (pi->cached_enable) { |
| 71 | + val |= PCI_CFG_ENABLE; |
| 72 | + } |
| 73 | + } |
| 74 | + } else if (is_cfg_data(addr)) { |
| 75 | + if (pi->cached_enable) { |
| 76 | + uint16_t offset = addr - 0xcfc; |
| 77 | + |
| 78 | + pci_vdev_cfg_handler(&vm->vpci, 1U, pi->cached_bdf, |
| 79 | + pi->cached_reg + offset, bytes, &val); |
| 80 | + |
| 81 | + pci_cfg_clear_cache(pi); |
| 82 | + } |
| 83 | + } else { |
| 84 | + val = 0xffffffffU; |
| 85 | + } |
| 86 | + |
| 87 | + return val; |
| 88 | +} |
| 89 | + |
| 90 | +static void pci_cfg_io_write(__unused struct vm_io_handler *hdlr, |
| 91 | + struct vm *vm, uint16_t addr, size_t bytes, uint32_t val) |
| 92 | +{ |
| 93 | + struct vpci *vpci = &vm->vpci; |
| 94 | + struct pci_addr_info *pi = &vpci->addr_info; |
| 95 | + |
| 96 | + if (is_cfg_addr(addr)) { |
| 97 | + /* TODO: handling the non 4 bytes access */ |
| 98 | + if (bytes == 4U) { |
| 99 | + pi->cached_bdf = PCI_BDF( |
| 100 | + ((val >> 16) & PCI_BUSMAX), |
| 101 | + ((val >> 11) & PCI_SLOTMAX), |
| 102 | + ((val >> 8) & PCI_FUNCMAX)); |
| 103 | + |
| 104 | + pi->cached_reg = val & PCI_REGMAX; |
| 105 | + pi->cached_enable = |
| 106 | + (val & PCI_CFG_ENABLE) == PCI_CFG_ENABLE; |
| 107 | + } |
| 108 | + } else if (is_cfg_data(addr)) { |
| 109 | + if (pi->cached_enable) { |
| 110 | + uint16_t offset = addr - 0xcfc; |
| 111 | + |
| 112 | + pci_vdev_cfg_handler(&vm->vpci, 0U, pi->cached_bdf, |
| 113 | + pi->cached_reg + offset, bytes, &val); |
| 114 | + |
| 115 | + pci_cfg_clear_cache(pi); |
| 116 | + } |
| 117 | + } else { |
| 118 | + pr_err("Not PCI cfg data/addr port access!"); |
| 119 | + } |
| 120 | + |
| 121 | +} |
| 122 | + |
| 123 | +void vpci_init(struct vm *vm) |
| 124 | +{ |
| 125 | + struct vpci *vpci = &vm->vpci; |
| 126 | + struct vpci_vdev_array *vdev_array; |
| 127 | + struct pci_vdev *vdev; |
| 128 | + int i; |
| 129 | + int ret; |
| 130 | + struct vm_io_range pci_cfg_range = {.flags = IO_ATTR_RW, |
| 131 | + .base = PCI_CONFIG_ADDR, .len = 8U}; |
| 132 | + |
| 133 | + vpci->vm = vm; |
| 134 | + vdev_array = vm->vm_desc->vpci_vdev_array; |
| 135 | + |
| 136 | + for (i = 0; i < vdev_array->num_pci_vdev; i++) { |
| 137 | + vdev = &vdev_array->vpci_vdev_list[i]; |
| 138 | + vdev->vpci = vpci; |
| 139 | + if ((vdev->ops != NULL) && (vdev->ops->init != NULL)) { |
| 140 | + ret = vdev->ops->init(vdev); |
| 141 | + if (ret) { |
| 142 | + pr_err("vdev->ops->init failed!"); |
| 143 | + } |
| 144 | + } |
| 145 | + } |
| 146 | + |
| 147 | + register_io_emulation_handler(vm, &pci_cfg_range, |
| 148 | + &pci_cfg_io_read, &pci_cfg_io_write); |
| 149 | +} |
| 150 | + |
| 151 | +void vpci_cleanup(struct vm *vm) |
| 152 | +{ |
| 153 | + struct vpci_vdev_array *vdev_array; |
| 154 | + struct pci_vdev *vdev; |
| 155 | + int i; |
| 156 | + int ret; |
| 157 | + |
| 158 | + vdev_array = vm->vm_desc->vpci_vdev_array; |
| 159 | + |
| 160 | + for (i = 0; i < vdev_array->num_pci_vdev; i++) { |
| 161 | + vdev = &vdev_array->vpci_vdev_list[i]; |
| 162 | + if ((vdev->ops != NULL) && (vdev->ops->deinit != NULL)) { |
| 163 | + ret = vdev->ops->deinit(vdev); |
| 164 | + if (ret) { |
| 165 | + pr_err("vdev->ops->deinit failed!"); |
| 166 | + } |
| 167 | + } |
| 168 | + } |
| 169 | +} |
| 170 | + |
| 171 | +void pci_vdev_cfg_handler(struct vpci *vpci, uint32_t in, uint16_t vbdf, |
| 172 | + uint32_t offset, uint32_t bytes, uint32_t *val) |
| 173 | +{ |
| 174 | + /* vm-exit handler */ |
| 175 | +} |
0 commit comments