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 struct acpi_mcfg_alloc {
25 } __attribute__((packed));
27 struct acpi_mcfg_table {
28 struct acpi_table_header header;
30 struct acpi_mcfg_alloc alloc_structs[];
31 } __attribute__((packed));
33 /* entry for PCI config space whitelist (granting access) */
34 struct pci_cfg_access {
35 u32 reg_num; /** Register number (4-byte aligned) */
36 u32 mask; /** Bit set: access allowed */
39 /* --- Whilelist for writing to PCI config space registers --- */
40 /* Type 1: Endpoints */
41 static const struct pci_cfg_access endpoint_write_access[] = {
42 { 0x04, 0xffffffff }, /* Command, Status */
43 { 0x0c, 0xff000000 }, /* BIST */
44 { 0x3c, 0x000000ff }, /* Int Line */
47 static const struct pci_cfg_access bridge_write_access[] = {
48 { 0x04, 0xffffffff }, /* Command, Status */
49 { 0x0c, 0xff000000 }, /* BIST */
50 { 0x3c, 0xffff00ff }, /* Int Line, Bridge Control */
53 static void *pci_space;
54 static u64 pci_mmcfg_addr;
55 static u32 pci_mmcfg_size;
58 * pci_get_assigned_device() - Look up device owned by a cell
60 * @bdf: 16-bit bus/device/function ID
62 * Return: Valid pointer - owns, NULL - doesn't own.
64 const struct jailhouse_pci_device *
65 pci_get_assigned_device(const struct cell *cell, u16 bdf)
67 const struct jailhouse_pci_device *device =
68 jailhouse_cell_pci_devices(cell->config);
71 for (n = 0; n < cell->config->num_pci_devices; n++)
72 if (((device[n].bus << 8) | device[n].devfn) == bdf)
79 * pci_cfg_read_moderate() - Moderate config space read access
80 * @cell: Request issuing cell
81 * @device: The device to be accessed; if NULL, access will be emulated,
82 * returning a value of -1
83 * @reg_num: Register number (4-byte aligned)
84 * @bias: Bias from register base address in bytes
85 * @size: Access size (1, 2 or 4 bytes)
86 * @value: Pointer to buffer to receive the emulated value if
87 * PCI_ACCESS_EMULATE is returned
89 * Return: PCI_ACCESS_PERFORM or PCI_ACCESS_EMULATE.
92 pci_cfg_read_moderate(const struct cell *cell,
93 const struct jailhouse_pci_device *device, u8 reg_num,
94 unsigned int reg_bias, unsigned int size, u32 *value)
98 return PCI_ACCESS_EMULATE;
101 if (reg_num < PCI_CONFIG_HEADER_SIZE)
102 return PCI_ACCESS_PERFORM;
104 // HACK: permit capability access until we properly filter it
105 return PCI_ACCESS_PERFORM;
109 * pci_cfg_write_moderate() - Moderate config space write access
110 * @cell: Request issuing cell
111 * @device: The device to be accessed; if NULL, access will be rejected
112 * @reg_num: Register number (4-byte aligned)
113 * @bias: Bias from register base address in bytes
114 * @size: Access size (1, 2 or 4 bytes)
115 * @value: Pointer to value to be written, initialized with cell value,
116 * set to the to-be-written hardware value if PCI_ACCESS_EMULATE
119 * Return: PCI_ACCESS_REJECT, PCI_ACCESS_PERFORM or PCI_ACCESS_EMULATE.
122 pci_cfg_write_moderate(const struct cell *cell,
123 const struct jailhouse_pci_device *device, u8 reg_num,
124 unsigned int reg_bias, unsigned int size, u32 *value)
126 /* initialize list to work around wrong compiler warning */
127 const struct pci_cfg_access *list = NULL;
128 unsigned int n, len = 0;
131 return PCI_ACCESS_REJECT;
133 if (reg_num < PCI_CONFIG_HEADER_SIZE) {
134 if (device->type == JAILHOUSE_PCI_TYPE_DEVICE) {
135 list = endpoint_write_access;
136 len = ARRAY_SIZE(endpoint_write_access);
137 } else if (device->type == JAILHOUSE_PCI_TYPE_BRIDGE) {
138 list = bridge_write_access;
139 len = ARRAY_SIZE(bridge_write_access);
142 for (n = 0; n < len; n++) {
143 if (list[n].reg_num == reg_num &&
144 ((list[n].mask >> (reg_bias * 8)) &
145 BYTE_MASK(size)) == BYTE_MASK(size))
146 return PCI_ACCESS_PERFORM;
148 return PCI_ACCESS_REJECT;
151 // HACK: permit capability access until we properly filter it
152 return PCI_ACCESS_PERFORM;
156 * pci_init() - Initialization of PCI module
158 * Return: 0 - success, error code - if error.
162 struct acpi_mcfg_table *mcfg;
164 mcfg = (struct acpi_mcfg_table *)acpi_find_table("MCFG", NULL);
168 if (mcfg->header.length !=
169 sizeof(struct acpi_mcfg_table) + sizeof(struct acpi_mcfg_alloc))
172 pci_mmcfg_addr = mcfg->alloc_structs[0].base_addr;
173 pci_mmcfg_size = (mcfg->alloc_structs[0].end_bus -
174 mcfg->alloc_structs[0].start_bus) * 256 * 4096;
175 pci_space = page_alloc(&remap_pool, pci_mmcfg_size / PAGE_SIZE);
179 return page_map_create(&hv_paging_structs,
180 mcfg->alloc_structs[0].base_addr,
181 pci_mmcfg_size, (unsigned long)pci_space,
182 PAGE_DEFAULT_FLAGS | PAGE_FLAG_UNCACHED,
183 PAGE_MAP_NON_COHERENT);
187 * pci_mmio_access_handler() - Handler for MMIO-accesses to PCI config space
188 * @cell: Request issuing cell
189 * @is_write: True if write access
190 * @addr: Address accessed
191 * @value: Pointer to value for reading/writing
193 * Return: 1 if handled successfully, 0 if unhandled, -1 on access error
195 int pci_mmio_access_handler(const struct cell *cell, bool is_write,
196 u64 addr, u32 *value)
198 u32 mmcfg_offset, val, reg_bias, reg_num;
199 const struct jailhouse_pci_device *device;
200 enum pci_access access;
202 if (!pci_space || addr < pci_mmcfg_addr ||
203 addr >= (pci_mmcfg_addr + pci_mmcfg_size - 4))
206 mmcfg_offset = addr - pci_mmcfg_addr;
207 reg_bias = mmcfg_offset % 4;
208 reg_num = mmcfg_offset & 0xfff;
209 device = pci_get_assigned_device(cell, mmcfg_offset >> 12);
213 access = pci_cfg_write_moderate(cell, device,
214 reg_num - reg_bias, reg_bias,
216 if (access == PCI_ACCESS_REJECT)
218 mmio_write32(pci_space + mmcfg_offset, val);
220 access = pci_cfg_read_moderate(cell, device,
221 reg_num - reg_bias, reg_bias,
223 if (access == PCI_ACCESS_PERFORM)
224 *value = mmio_read32(pci_space + mmcfg_offset);
230 panic_printk("FATAL: Invalid PCI MMCONFIG write, device %02x:%02x.%x, "
231 "reg: %\n", mmcfg_offset >> 20, (mmcfg_offset >> 15) & 31,
232 (mmcfg_offset >> 12) & 7, reg_num);