]> rtime.felk.cvut.cz Git - jailhouse.git/blob - hypervisor/pci.c
core: Add support for a guest to access memory-mapped PCI configuration space
[jailhouse.git] / hypervisor / pci.c
1 /*
2  * Jailhouse, a Linux-based partitioning hypervisor
3  *
4  * Copyright (c) Siemens AG, 2014
5  *
6  * Authors:
7  *  Ivan Kolchin <ivan.kolchin@siemens.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2.  See
10  * the COPYING file in the top-level directory.
11  */
12
13 #include <jailhouse/acpi.h>
14 #include <jailhouse/pci.h>
15 #include <jailhouse/printk.h>
16 #include <jailhouse/utils.h>
17
18 struct acpi_mcfg_alloc {
19         u64 base_addr;
20         u16 segment_num;
21         u8 start_bus;
22         u8 end_bus;
23         u32 reserved;
24 } __attribute__((packed));
25
26 struct acpi_mcfg_table {
27         struct acpi_table_header header;
28         u8 reserved[8];
29         struct acpi_mcfg_alloc alloc_structs[];
30 } __attribute__((packed));
31
32 /* entry for PCI config space whitelist (granting access) */
33 struct pci_cfg_access {
34         u32 reg_num; /** Register number (4-byte aligned) */
35         u32 mask; /** Bit set: access allowed */
36 };
37
38 /* --- Whilelist for writing to PCI config space registers --- */
39 /* Type 1: Endpoints */
40 static const struct pci_cfg_access endpoint_write_access[] = {
41         { 0x04, 0xffffffff }, /* Command, Status */
42         { 0x0c, 0xff000000 }, /* BIST */
43         { 0x3c, 0x000000ff }, /* Int Line */
44 };
45 /* Type 2: Bridges */
46 static const struct pci_cfg_access bridge_write_access[] = {
47         { 0x04, 0xffffffff }, /* Command, Status */
48         { 0x0c, 0xff000000 }, /* BIST */
49         { 0x3c, 0xffff00ff }, /* Int Line, Bridge Control */
50 };
51
52 static void *pci_space;
53 static u64 pci_mmcfg_addr;
54 static u32 pci_mmcfg_size;
55
56 /**
57  * pci_get_assigned_device() - Look up device owned by a cell
58  * @cell:       Owning cell
59  * @bdf:        16-bit bus/device/function ID
60  *
61  * Return: Valid pointer - owns, NULL - doesn't own.
62  */
63 const struct jailhouse_pci_device *
64 pci_get_assigned_device(const struct cell *cell, u16 bdf)
65 {
66         const struct jailhouse_pci_device *device =
67                 jailhouse_cell_pci_devices(cell->config);
68         u32 n;
69
70         for (n = 0; n < cell->config->num_pci_devices; n++)
71                 if (((device[n].bus << 8) | device[n].devfn) == bdf)
72                         return &device[n];
73
74         return NULL;
75 }
76
77 /**
78  * pci_cfg_write_allowed() - Check general config space write permission
79  * @type:       JAILHOUSE_PCI_TYPE_DEVICE or JAILHOUSE_PCI_TYPE_BRIDGE
80  * @reg_num:    Register number (4-byte aligned)
81  * @bias:       Bias from register base address in bytes
82  * @size:       Access size (1, 2 or 4 bytes)
83  *
84  * Return: True if writing is allowed, false otherwise.
85  */
86 bool pci_cfg_write_allowed(u32 type, u8 reg_num, unsigned int reg_bias,
87                            unsigned int size)
88 {
89         /* initialize list to work around wrong compiler warning */
90         const struct pci_cfg_access *list = NULL;
91         unsigned int n, len = 0;
92
93         if (type == JAILHOUSE_PCI_TYPE_DEVICE) {
94                 list = endpoint_write_access;
95                 len = ARRAY_SIZE(endpoint_write_access);
96         } else if (type == JAILHOUSE_PCI_TYPE_BRIDGE) {
97                 list = bridge_write_access;
98                 len = ARRAY_SIZE(bridge_write_access);
99         }
100
101         for (n = 0; n < len; n++)
102                 if (list[n].reg_num == reg_num)
103                         return ((list[n].mask >> (reg_bias * 8)) &
104                                  BYTE_MASK(size)) == BYTE_MASK(size);
105
106         return false;
107 }
108
109 /**
110  * pci_init() - Initialization of PCI module
111  *
112  * Return: 0 - success, error code - if error.
113  */
114 int pci_init(void)
115 {
116         struct acpi_mcfg_table *mcfg;
117
118         mcfg = (struct acpi_mcfg_table *)acpi_find_table("MCFG", NULL);
119         if (!mcfg)
120                 return 0;
121
122         if (mcfg->header.length !=
123             sizeof(struct acpi_mcfg_table) + sizeof(struct acpi_mcfg_alloc))
124                 return -EIO;
125
126         pci_mmcfg_addr = mcfg->alloc_structs[0].base_addr;
127         pci_mmcfg_size = (mcfg->alloc_structs[0].end_bus -
128                           mcfg->alloc_structs[0].start_bus) * 256 * 4096;
129         pci_space = page_alloc(&remap_pool, pci_mmcfg_size / PAGE_SIZE);
130         if (pci_space)
131                 page_map_create(&hv_paging_structs,
132                                 mcfg->alloc_structs[0].base_addr,
133                                 pci_mmcfg_size, (unsigned long)pci_space,
134                                 PAGE_DEFAULT_FLAGS | PAGE_FLAG_UNCACHED,
135                                 PAGE_MAP_NON_COHERENT);
136
137         return 0;
138 }
139
140 /**
141  * pci_mmio_access_handler() - Handler for MMIO-accesses to PCI config space
142  * @cell:       Request issuing cell
143  * @is_write:   True if write access
144  * @addr:       Address accessed
145  * @value:      Value to write (for write operations only)
146  *
147  * Return: 1 if handled successfully, 0 if unhandled, -1 on access error
148  */
149 int pci_mmio_access_handler(struct registers *guest_regs,
150                             const struct cell *cell, bool is_write,
151                             u64 addr, u32 reg)
152 {
153         const struct jailhouse_pci_device *device;
154         u32 mmcfg_offset;
155         u32 reg_num;
156         u32 reg_bias;
157
158         if (addr < pci_mmcfg_addr ||
159             addr >= (pci_mmcfg_addr + pci_mmcfg_size - 4))
160                 return 0;
161
162         mmcfg_offset = addr - pci_mmcfg_addr;
163         reg_bias = mmcfg_offset % 4;
164         reg_num = mmcfg_offset & 0xfff;
165         device = pci_get_assigned_device(cell, mmcfg_offset >> 12);
166
167         if (is_write) {
168                 if (!device)
169                         return 1;
170
171                 if (reg_num < PCI_CONFIG_HEADER_SIZE)
172                         if (pci_cfg_write_allowed(device->type,
173                                                   (reg_num - reg_bias),
174                                                   reg_bias, 4))
175                                 *(volatile u32 *)(pci_space + mmcfg_offset) =
176                                         ((u64 *)guest_regs)[reg];
177         } else
178                 if (device)
179                         ((u64 *)guest_regs)[reg] =
180                                 *(volatile u32 *)(pci_space + mmcfg_offset);
181                 else
182                         ((u64 *)guest_regs)[reg] = BYTE_MASK(4);
183
184         return 1;
185 }