2 * Jailhouse, a Linux-based partitioning hypervisor
4 * Copyright (c) Siemens AG, 2014
7 * Ivan Kolchin <ivan.kolchin@siemens.com>
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
13 #include <jailhouse/acpi.h>
14 #include <jailhouse/mmio.h>
15 #include <jailhouse/pci.h>
16 #include <jailhouse/printk.h>
17 #include <jailhouse/utils.h>
19 #define PCI_CONFIG_HEADER_SIZE 0x40
21 struct acpi_mcfg_alloc {
27 } __attribute__((packed));
29 struct acpi_mcfg_table {
30 struct acpi_table_header header;
32 struct acpi_mcfg_alloc alloc_structs[];
33 } __attribute__((packed));
35 /* entry for PCI config space whitelist (granting access) */
36 struct pci_cfg_access {
37 u32 reg_num; /** Register number (4-byte aligned) */
38 u32 mask; /** Bit set: access allowed */
41 /* --- Whilelist for writing to PCI config space registers --- */
42 /* Type 1: Endpoints */
43 static const struct pci_cfg_access endpoint_write_access[] = {
44 { 0x04, 0xffffffff }, /* Command, Status */
45 { 0x0c, 0xff00ffff }, /* BIST, Latency Timer, Cacheline */
46 { 0x3c, 0x000000ff }, /* Int Line */
49 static const struct pci_cfg_access bridge_write_access[] = {
50 { 0x04, 0xffffffff }, /* Command, Status */
51 { 0x0c, 0xff00ffff }, /* BIST, Latency Timer, Cacheline */
52 { 0x3c, 0xffff00ff }, /* Int Line, Bridge Control */
55 static void *pci_space;
56 static u64 pci_mmcfg_addr;
57 static u32 pci_mmcfg_size;
60 * pci_get_assigned_device() - Look up device owned by a cell
62 * @bdf: 16-bit bus/device/function ID
64 * Return: Valid pointer - owns, NULL - doesn't own.
66 const struct jailhouse_pci_device *
67 pci_get_assigned_device(const struct cell *cell, u16 bdf)
69 const struct jailhouse_pci_device *device =
70 jailhouse_cell_pci_devices(cell->config);
73 for (n = 0; n < cell->config->num_pci_devices; n++)
74 if (device[n].bdf == bdf)
81 * pci_find_capability() - Look up capability at given config space address
82 * @cell: Device owning cell
83 * @device: The device to be accessed
84 * @address: Config space access address
86 * Return: Corresponding capability structure or NULL if none found.
88 static const struct jailhouse_pci_capability *
89 pci_find_capability(const struct cell *cell,
90 const struct jailhouse_pci_device *device, u16 address)
92 const struct jailhouse_pci_capability *cap =
93 jailhouse_cell_pci_caps(cell->config) + device->caps_start;
96 for (n = 0; n < device->num_caps; n++, cap++)
97 if (cap->start <= address && cap->start + cap->len > address)
104 * pci_cfg_read_moderate() - Moderate config space read access
105 * @cell: Request issuing cell
106 * @device: The device to be accessed; if NULL, access will be emulated,
107 * returning a value of -1
108 * @address: Config space address
109 * @size: Access size (1, 2 or 4 bytes)
110 * @value: Pointer to buffer to receive the emulated value if
111 * PCI_ACCESS_EMULATE is returned
113 * Return: PCI_ACCESS_PERFORM or PCI_ACCESS_EMULATE.
116 pci_cfg_read_moderate(const struct cell *cell,
117 const struct jailhouse_pci_device *device, u16 address,
118 unsigned int size, u32 *value)
120 const struct jailhouse_pci_capability *cap;
124 return PCI_ACCESS_EMULATE;
127 if (address < PCI_CONFIG_HEADER_SIZE)
128 return PCI_ACCESS_PERFORM;
130 cap = pci_find_capability(cell, device, address);
132 return PCI_ACCESS_PERFORM;
134 // TODO: Emulate MSI/MSI-X etc.
136 return PCI_ACCESS_PERFORM;
140 * pci_cfg_write_moderate() - Moderate config space write access
141 * @cell: Request issuing cell
142 * @device: The device to be accessed; if NULL, access will be rejected
143 * @address: Config space address
144 * @size: Access size (1, 2 or 4 bytes)
145 * @value: Pointer to value to be written, initialized with cell value,
146 * set to the to-be-written hardware value if PCI_ACCESS_EMULATE
149 * Return: PCI_ACCESS_REJECT, PCI_ACCESS_PERFORM or PCI_ACCESS_EMULATE.
152 pci_cfg_write_moderate(const struct cell *cell,
153 const struct jailhouse_pci_device *device, u16 address,
154 unsigned int size, u32 *value)
156 const struct jailhouse_pci_capability *cap;
157 /* initialize list to work around wrong compiler warning */
158 const struct pci_cfg_access *list = NULL;
159 unsigned int n, bias_shift, len = 0;
163 return PCI_ACCESS_REJECT;
165 if (address < PCI_CONFIG_HEADER_SIZE) {
166 if (device->type == JAILHOUSE_PCI_TYPE_DEVICE) {
167 list = endpoint_write_access;
168 len = ARRAY_SIZE(endpoint_write_access);
169 } else if (device->type == JAILHOUSE_PCI_TYPE_BRIDGE) {
170 list = bridge_write_access;
171 len = ARRAY_SIZE(bridge_write_access);
174 bias_shift = (address & 0x003) * 8;
175 mask = BYTE_MASK(size);
177 for (n = 0; n < len; n++) {
178 if (list[n].reg_num == (address & 0xffc) &&
179 ((list[n].mask >> bias_shift) & mask) == mask)
180 return PCI_ACCESS_PERFORM;
183 return PCI_ACCESS_REJECT;
186 cap = pci_find_capability(cell, device, address);
187 if (!cap || !(cap->flags & JAILHOUSE_PCICAPS_WRITE))
188 return PCI_ACCESS_REJECT;
190 return PCI_ACCESS_PERFORM;
194 * pci_init() - Initialization of PCI module
196 * Return: 0 - success, error code - if error.
200 struct acpi_mcfg_table *mcfg;
202 mcfg = (struct acpi_mcfg_table *)acpi_find_table("MCFG", NULL);
206 if (mcfg->header.length !=
207 sizeof(struct acpi_mcfg_table) + sizeof(struct acpi_mcfg_alloc))
210 pci_mmcfg_addr = mcfg->alloc_structs[0].base_addr;
211 pci_mmcfg_size = (mcfg->alloc_structs[0].end_bus + 1) * 256 * 4096;
212 pci_space = page_alloc(&remap_pool, pci_mmcfg_size / PAGE_SIZE);
216 return page_map_create(&hv_paging_structs,
217 mcfg->alloc_structs[0].base_addr,
218 pci_mmcfg_size, (unsigned long)pci_space,
219 PAGE_DEFAULT_FLAGS | PAGE_FLAG_UNCACHED,
220 PAGE_MAP_NON_COHERENT);
224 * pci_mmio_access_handler() - Handler for MMIO-accesses to PCI config space
225 * @cell: Request issuing cell
226 * @is_write: True if write access
227 * @addr: Address accessed
228 * @value: Pointer to value for reading/writing
230 * Return: 1 if handled successfully, 0 if unhandled, -1 on access error
232 int pci_mmio_access_handler(const struct cell *cell, bool is_write,
233 u64 addr, u32 *value)
235 u32 mmcfg_offset, val, reg_addr;
236 const struct jailhouse_pci_device *device;
237 enum pci_access access;
239 if (!pci_space || addr < pci_mmcfg_addr ||
240 addr >= (pci_mmcfg_addr + pci_mmcfg_size - 4))
243 mmcfg_offset = addr - pci_mmcfg_addr;
244 reg_addr = mmcfg_offset & 0xfff;
245 device = pci_get_assigned_device(cell, mmcfg_offset >> 12);
249 access = pci_cfg_write_moderate(cell, device, reg_addr, 4,
251 if (access == PCI_ACCESS_REJECT)
253 mmio_write32(pci_space + mmcfg_offset, val);
255 access = pci_cfg_read_moderate(cell, device, reg_addr, 4,
257 if (access == PCI_ACCESS_PERFORM)
258 *value = mmio_read32(pci_space + mmcfg_offset);
264 panic_printk("FATAL: Invalid PCI MMCONFIG write, device %02x:%02x.%x, "
265 "reg: %\n", PCI_BDF_PARAMS(mmcfg_offset >> 12), reg_addr);