]> rtime.felk.cvut.cz Git - jailhouse.git/blob - hypervisor/pci.c
core: Avoid exposing register set to pci_mmio_access_handler
[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/mmio.h>
15 #include <jailhouse/pci.h>
16 #include <jailhouse/printk.h>
17 #include <jailhouse/utils.h>
18
19 struct acpi_mcfg_alloc {
20         u64 base_addr;
21         u16 segment_num;
22         u8 start_bus;
23         u8 end_bus;
24         u32 reserved;
25 } __attribute__((packed));
26
27 struct acpi_mcfg_table {
28         struct acpi_table_header header;
29         u8 reserved[8];
30         struct acpi_mcfg_alloc alloc_structs[];
31 } __attribute__((packed));
32
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 */
37 };
38
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 */
45 };
46 /* Type 2: Bridges */
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 */
51 };
52
53 static void *pci_space;
54 static u64 pci_mmcfg_addr;
55 static u32 pci_mmcfg_size;
56
57 /**
58  * pci_get_assigned_device() - Look up device owned by a cell
59  * @cell:       Owning cell
60  * @bdf:        16-bit bus/device/function ID
61  *
62  * Return: Valid pointer - owns, NULL - doesn't own.
63  */
64 const struct jailhouse_pci_device *
65 pci_get_assigned_device(const struct cell *cell, u16 bdf)
66 {
67         const struct jailhouse_pci_device *device =
68                 jailhouse_cell_pci_devices(cell->config);
69         u32 n;
70
71         for (n = 0; n < cell->config->num_pci_devices; n++)
72                 if (((device[n].bus << 8) | device[n].devfn) == bdf)
73                         return &device[n];
74
75         return NULL;
76 }
77
78 /**
79  * pci_cfg_write_allowed() - Check general config space write permission
80  * @type:       JAILHOUSE_PCI_TYPE_DEVICE or JAILHOUSE_PCI_TYPE_BRIDGE
81  * @reg_num:    Register number (4-byte aligned)
82  * @bias:       Bias from register base address in bytes
83  * @size:       Access size (1, 2 or 4 bytes)
84  *
85  * Return: True if writing is allowed, false otherwise.
86  */
87 bool pci_cfg_write_allowed(u32 type, u8 reg_num, unsigned int reg_bias,
88                            unsigned int size)
89 {
90         /* initialize list to work around wrong compiler warning */
91         const struct pci_cfg_access *list = NULL;
92         unsigned int n, len = 0;
93
94         if (type == JAILHOUSE_PCI_TYPE_DEVICE) {
95                 list = endpoint_write_access;
96                 len = ARRAY_SIZE(endpoint_write_access);
97         } else if (type == JAILHOUSE_PCI_TYPE_BRIDGE) {
98                 list = bridge_write_access;
99                 len = ARRAY_SIZE(bridge_write_access);
100         }
101
102         for (n = 0; n < len; n++)
103                 if (list[n].reg_num == reg_num)
104                         return ((list[n].mask >> (reg_bias * 8)) &
105                                  BYTE_MASK(size)) == BYTE_MASK(size);
106
107         return false;
108 }
109
110 /**
111  * pci_init() - Initialization of PCI module
112  *
113  * Return: 0 - success, error code - if error.
114  */
115 int pci_init(void)
116 {
117         struct acpi_mcfg_table *mcfg;
118
119         mcfg = (struct acpi_mcfg_table *)acpi_find_table("MCFG", NULL);
120         if (!mcfg)
121                 return 0;
122
123         if (mcfg->header.length !=
124             sizeof(struct acpi_mcfg_table) + sizeof(struct acpi_mcfg_alloc))
125                 return -EIO;
126
127         pci_mmcfg_addr = mcfg->alloc_structs[0].base_addr;
128         pci_mmcfg_size = (mcfg->alloc_structs[0].end_bus -
129                           mcfg->alloc_structs[0].start_bus) * 256 * 4096;
130         pci_space = page_alloc(&remap_pool, pci_mmcfg_size / PAGE_SIZE);
131         if (pci_space)
132                 page_map_create(&hv_paging_structs,
133                                 mcfg->alloc_structs[0].base_addr,
134                                 pci_mmcfg_size, (unsigned long)pci_space,
135                                 PAGE_DEFAULT_FLAGS | PAGE_FLAG_UNCACHED,
136                                 PAGE_MAP_NON_COHERENT);
137
138         return 0;
139 }
140
141 /**
142  * pci_mmio_access_handler() - Handler for MMIO-accesses to PCI config space
143  * @cell:       Request issuing cell
144  * @is_write:   True if write access
145  * @addr:       Address accessed
146  * @value:      Pointer to value for reading/writing
147  *
148  * Return: 1 if handled successfully, 0 if unhandled, -1 on access error
149  */
150 int pci_mmio_access_handler(const struct cell *cell, bool is_write,
151                             u64 addr, u32 *value)
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                                 mmio_write32(pci_space + mmcfg_offset, *value);
176         } else
177                 if (device)
178                         *value = mmio_read32(pci_space + mmcfg_offset);
179                 else
180                         *value = -1;
181
182         return 1;
183 }