1 /*-
2 * Copyright (c) 2011 NetApp, Inc.
3 * Copyright (c) 2018-2024 Intel Corporation.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28 
29 
30 /*_
31 * Emulate a PCI Host bridge:
32 * Intel Corporation Celeron N3350/Pentium N4200/Atom E3900
33 * Series Host Bridge (rev 0b)
34 */
35 
36 #include <asm/guest/vm.h>
37 #include <pci.h>
38 #include "vpci_priv.h"
39 #include <vacpi.h>
40 
41 /**
42  * @addtogroup vp-dm_vperipheral
43  *
44  * @{
45  */
46 
47 /**
48  * @file
49  * @brief Implementation of virtual PCI host bridge.
50  *
51  * This file defines operations to support virtual PCI host bridge. All the operation related APIs are registered as the
52  * callbacks in the global variable "struct pci_vdev_ops vhostbridge_ops".
53  */
54 
55 /*
56 The chart below shows the hostbridge DID high-byte of the platform later than broadwell, whose PCIEXBAR are always
57 located in the PCI hostbridge config space at the offset 0x60. This chart may need further extension in the future
58 --------------------------------------------------------------------------------------
59 	platform			|	hostbridge DID high-byte
60 --------------------------------------------------------------------------------------
61 	SKL(6-gen)			|	0x19
62 	APL(6-gen Atom)			|	0x5a
63 	KBL(7/8-gen)			|	0x59
64 	CFL/CFL-R(8/9-gen)		|	0x3e
65 	ICL(10-gen)			|	0x9b
66 	EHL(11-gen)			|	0x45
67 	TGL(11-gen)			|	0x9a
68 --------------------------------------------------------------------------------------
69 */
70 static const uint32_t hostbridge_did_highbytes[] = {0x19U, 0x5aU, 0x59U, 0x3eU, 0x9aU, 0x45U, 0x9bU};
71 
72 /*
73 TODO:
74 1. In the future, we may add one or more virtual hostbridges for CPUs that are incompatible in layout with the current
75 one
76 2. Besides PCIEXBAR(0x60), there are also some registers needs to be emulated more precisely rather than be treated as
77 read-only and hard-coded, listed below:
78 
79 -----------------------------------------------------------------------------------------------
80 reg		|offset	|length	|current status	|remark
81 -----------------------------------------------------------------------------------------------
82 STATUS_COMMAND	|0x8	|dword	|unemulated	|pci status and command
83 SVID_SID	|0x2C	|dword	|unemulated	|subsys id and subsys vendor id
84 MCHBAR		|0x48	|qword	|hard-coded	|BAR of memory controller hub
85 GGC		|0x50	|dword	|hard-coded	|graphics & mem controller hub graphics CR
86 DEVEN		|0x54	|dword	|hard-coded	|device enable register
87 PAVPC		|0x58	|dword	|hard-coded	|protected audio video path control
88 TOUUD		|0xA8	|qword	|hard-coded	|top of upper usable DRAM
89 BDSM		|0xB0	|dword	|hard-coded	|base of data stolen memory
90 BGSM		|0xB4	|dword	|hard-coded	|base of graphics stolen memory
91 TSEGMB		|0xB8	|dword	|hard-coded	|top segmentmemory base
92 TOLUD		|0xBC	|dword	|hard-coded	|top of lower usable dram
93 SKPD		|0xDC	|dword	|unemulated	|scratchpad
94 CAPID0_CAPCTRL0	|0xe0	|dword	|hard-coded	|capability 0 control
95 -----------------------------------------------------------------------------------------------
96 */
97 
98 /**
99  * @brief Initialize the virtual host bridge.
100  *
101  * A host bridge is a PCI device that is used to support the pci devices under it. This function initializes the
102  * specified virtual PCI device as a host bridge. It's usually called during the initialization of a VM.
103  *
104  * This function emulates the virtual host bridge as a "Celeron N3350/Pentium N4200/Atom E3900 Series Host Bridge",
105  * which belongs to Intel Apollo Lake processors family, and the device id is 0x5af0. Per "Section 9 C-Unit in Intel®
106  * Pentium® and Celeron® Processor N- and J- Series, Datasheet Volume 2", it initializes related type info registers in
107  * configuration space.
108  * PCI Express Enhanced Configuration Range Base Address Register (PCIEXBAR) is emulated differently for pre-launched
109  * VMs and Service VM, to support PCI Express Enhanced Configuration Access Mechanism (ECAM).
110  * - For a pre-launched VM, it is emulated as 'USER_VM_VIRT_PCI_MMCFG_BASE | 0x1'. USER_VM_VIRT_PCI_MMCFG_BASE
111  *   (0xE0000000) is the hard-coded virtual PCI MMCFG address base for pre/post-launched VMs. Bit 0 is set to 1 to
112  *   indicate that the base address defined in the PCIEXBAR register is active.
113  * - For a Service VM, it is emulated to be the same value as the physical PCIEXBAR. It is not used for now, mainly for
114  *   feature extension in the future.
115  * Finally, it sets the field parent_user to NULL and the field user to vdev, indicating that this vPCI bridge is used
116  * by a VM.
117  *
118  * @param[inout] vdev Pointer to the virtual PCI device to be initialized.
119  *
120  * @return None
121  *
122  * @pre vdev != NULL
123  * @pre vdev->vpci != NULL
124  *
125  * @post N/A
126  */
init_vhostbridge(struct pci_vdev * vdev)127 static void init_vhostbridge(struct pci_vdev *vdev)
128 {
129 	union pci_bdf hostbridge_bdf = {.value = 0x0U};
130 	uint32_t pciexbar_low = 0x0U, pciexbar_high = 0x0U, phys_did, i;
131 	/* Refer to Section 9 C-Unit in Intel® Pentium® and Celeron® Processor N- and J- Series, Datasheet Volume 2 */
132 	/* PCI config space */
133 	pci_vdev_write_vcfg(vdev, PCIR_VENDOR, 2U, 0x8086U);
134 	pci_vdev_write_vcfg(vdev, PCIR_DEVICE, 2U, 0x5af0U);
135 	pci_vdev_write_vcfg(vdev, PCIR_REVID, 1U, 0xbU);
136 	pci_vdev_write_vcfg(vdev, PCIR_SUBCLASS, 1U, PCIS_BRIDGE_HOST);
137 	pci_vdev_write_vcfg(vdev, PCIR_CLASS, 1U, PCIC_BRIDGE);
138 	pci_vdev_write_vcfg(vdev, PCIR_HDRTYPE, 1U, (PCIM_HDRTYPE_NORMAL | PCIM_MFDEV));
139 	/* First Capability Register is CAPID0_CAPCTRL0 */
140 	pci_vdev_write_vcfg(vdev, PCIR_CAP_PTR, 1U, 0xe0U);
141 	pci_vdev_write_vcfg(vdev, PCIR_INTERRUPT_LINE, 1U, 0xe0U);
142 
143 	/* Memory Controller Hub Base Address Register, MCHBAR_LO */
144 	/* MCHBAR[38:15] is {MCHBAR_HI[6:0],MCHBAR_LO[31:15]} */
145 	pci_vdev_write_vcfg(vdev, 0x48U, 4U, 0xfed10001U);
146 	/* Graphics and Memory Controller Hub Graphics Control Register, GGC */
147 	/* [15:8] is Graphics Memory Select (GMS), 512MB */
148 	pci_vdev_write_vcfg(vdev, 0x50U, 4U, 0x000002c1U);
149 	/* Device Enable Register, DEVEN */
150 	pci_vdev_write_vcfg(vdev, 0x54U, 4U, 0x00000033U);
151 	/* Protected Audio Video Path Control, PAVPC */
152 	pci_vdev_write_vcfg(vdev, 0x58U, 4U, 0x7ff00007U);
153 	/* Top of Upper Usable DRAM Low, TOUUD_LO */
154 	pci_vdev_write_vcfg(vdev, 0xa8U, 4U, 0x80000000U);
155 	/* Top of Upper Usable DRAM High, TOUUD_HI */
156 	pci_vdev_write_vcfg(vdev, 0xacU, 4U, 0x00000002U);
157 	/* Base of Data Stolen Memory, BDSM */
158 	pci_vdev_write_vcfg(vdev, 0xb0U, 4U, 0x7c000001U);
159 	/* Base of Graphics Stolen Memory, BGSM */
160 	pci_vdev_write_vcfg(vdev, 0xb4U, 4U, 0x7b800001U);
161 	/* Top Segment Memory Base, TSEGMB */
162 	pci_vdev_write_vcfg(vdev, 0xb8U, 4U, 0x7b000001U);
163 	/* Top of Lower Usable DRAM, TOLUD */
164 	pci_vdev_write_vcfg(vdev, 0xbcU, 4U, 0x80000001U);
165 	/* Capability ID0 Capability Control, CAPID0_CAPCTRL0 */
166 	/* CAP_ID: 9h, NEXT_CAP: 0h, CAPIDLEN: Ch, CAPID_VER: 1h */
167 	pci_vdev_write_vcfg(vdev, 0xe0U, 4U, 0x010c0009U);
168 	pci_vdev_write_vcfg(vdev, 0xf4U, 4U, 0x011c0f00U);
169 
170 	if (is_prelaunched_vm(container_of(vdev->vpci, struct acrn_vm, vpci))) {
171 		/* For pre-launched VMs, we only need to write an GPA that's reserved in guest ve820,
172 		 * and USER_VM_VIRT_PCI_MMCFG_BASE(0xE0000000) is fine. The trailing 1 is a ECAM enable-bit
173 		 */
174 		pciexbar_low = USER_VM_VIRT_PCI_MMCFG_BASE | 0x1U;
175 	} else {
176 		/* Inject physical ECAM value to Service VM vhostbridge since Service VM may check PCIe-MMIO Base
177 		Address with it */
178 		phys_did = pci_pdev_read_cfg(hostbridge_bdf, PCIR_DEVICE, 2);
179 		for (i = 0U; i < (sizeof(hostbridge_did_highbytes) / sizeof(uint32_t)); i++) {
180 			if (((phys_did & 0xff00U) >> 8) == hostbridge_did_highbytes[i]) {
181 				/* The offset of PCIEXBAR register is 0x60 on Intel platforms, and no counter-case is
182 				encountered yet */
183 				pciexbar_low = pci_pdev_read_cfg(hostbridge_bdf, 0x60U, 4);
184 				pciexbar_high = pci_pdev_read_cfg(hostbridge_bdf, 0x64U, 4);
185 				break;
186 			}
187 		}
188 	}
189 	/* PCI Express Enhanced Configuration Range Base Address Low, PCIEXBAR_LO */
190 	pci_vdev_write_vcfg(vdev, 0x60U, 4, pciexbar_low);
191 	/* PCI Express Enhanced Configuration Range Base Address High, PCIEXBAR_HI */
192 	pci_vdev_write_vcfg(vdev, 0x64U, 4, pciexbar_high);
193 	vdev->parent_user = NULL;
194 	vdev->user = vdev;
195 }
196 
197 /**
198  * @brief Deinitialize the virtual host bridge.
199  *
200  * This function deinitializes the specified virtual PCI device that was previously initialized as a host bridge.
201  *
202  * For the specified vdev, it sets the fields parent_user and user to NULL, indicating that this virtual device is not
203  * owned by any VM.
204  *
205  * @param[inout] vdev Pointer to the virtual PCI device.
206  *
207  * @return None
208  *
209  * @pre vdev != NULL
210  *
211  * @post N/A
212  */
deinit_vhostbridge(struct pci_vdev * vdev)213 static void deinit_vhostbridge(struct pci_vdev *vdev)
214 {
215 	vdev->parent_user = NULL;
216 	vdev->user = NULL;
217 }
218 
219 /**
220  * @brief Read the configuration space of the virtual host bridge.
221  *
222  * This function reads the configuration space of the specified virtual PCI device that is configured as a host bridge.
223  * It is used to retrieve specific configuration data of the virtual host bridge for further processing or validation.
224  *
225  * It reads the configuration space of the virtual host bridge and stores the read configuration data in the provided
226  * buffer.
227  *
228  * @param[in] vdev Pointer to the virtual PCI device whose configuration space is to be read.
229  * @param[in] offset Offset within the configuration space to read from.
230  * @param[in] bytes Number of bytes to read from the configuration space.
231  * @param[inout] val Pointer to the buffer where the read configuration data will be stored.
232  *
233  * @return Always return 0.
234  *
235  * @pre vdev != NULL
236  * @pre val != NULL
237  *
238  * @post retval == 0
239  */
read_vhostbridge_cfg(struct pci_vdev * vdev,uint32_t offset,uint32_t bytes,uint32_t * val)240 static int32_t read_vhostbridge_cfg(struct pci_vdev *vdev, uint32_t offset,
241 	uint32_t bytes, uint32_t *val)
242 {
243 	*val = pci_vdev_read_vcfg(vdev, offset, bytes);
244 	return 0;
245 }
246 
247 /**
248  * @brief Write to the virtual host bridge configuration space.
249  *
250  * This function writes to the configuration space of the specified virtual PCI device that is configured as a host
251  * bridge. It is used to update specific configuration settings based on the provided parameters.
252  *
253  * For the non-BAR configuration space, it writes the provided value to the configuration space of the virtual host
254  * bridge. For the BAR configuration space, it is read-only and the write operation is ignored.
255  *
256  * @param[inout] vdev Pointer to the virtual PCI device whose configuration space is to be written.
257  * @param[in] offset Offset within the configuration space to start writing to.
258  * @param[in] bytes Number of bytes to write to the configuration space.
259  * @param[in] val Value to be written to the configuration space.
260  *
261  * @return Always return 0.
262  *
263  * @pre vdev != NULL
264  *
265  * @post retval == 0
266  */
write_vhostbridge_cfg(struct pci_vdev * vdev,uint32_t offset,uint32_t bytes,uint32_t val)267 static int32_t write_vhostbridge_cfg(struct pci_vdev *vdev, uint32_t offset,
268 	uint32_t bytes, uint32_t val)
269 {
270 	if (!is_bar_offset(PCI_BAR_COUNT, offset)) {
271 		pci_vdev_write_vcfg(vdev, offset, bytes, val);
272 	}
273 	return 0;
274 }
275 
276 /**
277  * @brief Data structure implementation for virtual host bridge operations.
278  *
279  * Struct pci_vdev_ops is used to define the operations of virtual PCI device and definition here is used to support
280  * virtual host bridge.
281  *
282  * A pre-launched VM may have some pci devices and a host bridge is needed to support these devices. This struct is used
283  * to define the operations of virtual host bridge in this case for now.
284  *
285  * @consistency N/A
286  * @alignment N/A
287  *
288  * @remark N/A
289  */
290 const struct pci_vdev_ops vhostbridge_ops = {
291 	.init_vdev	= init_vhostbridge,
292 	.deinit_vdev	= deinit_vhostbridge,
293 	.write_vdev_cfg	= write_vhostbridge_cfg,
294 	.read_vdev_cfg	= read_vhostbridge_cfg,
295 };
296 
297 /**
298  * @}
299  */